From 7ad9c3c93f0cc103347ae2e2429e0122fb512e24 Mon Sep 17 00:00:00 2001 From: lrj <owen.stl@gmail.com> Date: 星期三, 01 十月 2025 21:26:12 +0800 Subject: [PATCH] feat: 修复员工管理功能并优化UI --- web/src/api/projectReview.js | 32 web/test-employee-roles.html | 198 ++++++ web/src/views/judge-review-list.vue | 0 web/src/api/player.js | 20 web/src/views/review-detail.vue | 0 fix-roles.ps1 | 81 ++ web/src/api/activity.js | 17 web/src/views/employee-list.vue | 60 + web/fix-employee-roles.html | 307 +++++++++ web/src/views/judge-list.vue | 0 web/src/utils/cos.ts | 1 web/test-role-api.html | 68 ++ web/src/config/api.ts | 34 web/src/views/activity-list.vue | 0 web/src/api/carousel.js | 16 web/src/api/projectReviewNew.js | 5 web/src/views/rating-detail.vue | 0 web/src/views/employee-detail.vue | 0 web/src/views/next-list.vue | 0 web/src/api/rating.js | 8 web/src/utils/cos-simple.ts | 7 web/src/api/media.js | 53 - backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java | 28 web/src/api/promotion.js | 8 web/src/api/role.ts | 24 web/src/views/review-list.vue | 0 web/src/views/ActivityForm.vue | 225 ++++-- backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java | 11 /dev/null | 254 -------- web/src/api/employee.ts | 24 web/src/utils/upload-logo-browser.ts | 8 web/check-auth.html | 128 ++++ web/src/views/check-detail.vue | 0 web/src/api/region.js | 20 web/debug-roles.html | 99 +++ web/src/router/index.ts | 54 web/src/components/JudgeFormSimple.vue | 2 web/src/views/check-list.vue | 8 fix_employee_roles.sql | 27 web/src/views/judge-review-detail.vue | 0 web/src/views/rating-list.vue | 0 41 files changed, 1,287 insertions(+), 540 deletions(-) diff --git a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java index b692385..75bebca 100644 --- a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java +++ b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java @@ -16,6 +16,8 @@ import com.rongyichuang.common.dto.PageResponse; import com.rongyichuang.rating.entity.RatingScheme; import com.rongyichuang.rating.repository.RatingSchemeRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; @@ -37,6 +39,8 @@ @Service @Transactional public class ActivityService { + + private static final Logger log = LoggerFactory.getLogger(ActivityService.class); @Autowired private ActivityRepository activityRepository; @@ -185,6 +189,9 @@ // 淇濆瓨姣旇禌 activity = activityRepository.save(activity); + // 璁板綍鏃ュ織浠ヤ究璋冭瘯 + log.info("淇濆瓨姣旇禌鎴愬姛锛屾瘮璧汭D: {}, 姣旇禌鍚嶇О: {}", activity.getId(), activity.getName()); + // 濡傛灉鏄瘮璧涗笖鏈夐樁娈典俊鎭紝淇濆瓨闃舵 if (input.isMainActivity() && input.getStages() != null && !input.getStages().isEmpty()) { saveActivityStages(activity.getId(), input.getStages()); @@ -264,7 +271,11 @@ stage.setRatingSchemeId(activity.getRatingSchemeId()); } - activityRepository.save(stage); + // 淇濆瓨闃舵骞惰幏鍙栬嚜澧濱D + stage = activityRepository.save(stage); + + // 璁板綍鏃ュ織浠ヤ究璋冭瘯 + log.info("淇濆瓨闃舵鎴愬姛锛岄樁娈礗D: {}, 闃舵鍚嶇О: {}", stage.getId(), stage.getName()); } } @@ -273,14 +284,20 @@ */ private void saveActivityJudges(Long activityId, List<ActivityJudgeInput> judgeInputs) { if (judgeInputs == null || judgeInputs.isEmpty()) { + log.info("娌℃湁璇勫闇�瑕佷繚瀛橈紝姣旇禌ID: {}", activityId); return; } + + log.info("寮�濮嬩繚瀛樿瘎濮旓紝姣旇禌ID: {}, 璇勫鏁伴噺: {}", activityId, judgeInputs.size()); // 鑾峰彇姣旇禌鐨勬墍鏈夐樁娈碉紙濡傛灉鏈夌殑璇濓級 List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(activityId, 1); // 淇濆瓨璇勫鍏宠仈 for (ActivityJudgeInput judgeInput : judgeInputs) { + log.info("澶勭悊璇勫: ID={}, 濮撳悕={}, 闃舵IDs={}", + judgeInput.getJudgeId(), judgeInput.getJudgeName(), judgeInput.getStageIds()); + // 鍏堝垹闄よ璇勫鐨勭幇鏈夊叧鑱� activityJudgeRepository.deleteByActivityIdAndJudgeId(activityId, judgeInput.getJudgeId()); @@ -289,12 +306,14 @@ if (stages.isEmpty()) { // 姣旇禌娌℃湁闃舵锛岀洿鎺ュ叧鑱斿埌姣旇禌锛坰tage_id涓簄ull琛ㄧず鎵�鏈夐樁娈碉級 ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), null); - activityJudgeRepository.save(activityJudge); + activityJudge = activityJudgeRepository.save(activityJudge); + log.info("淇濆瓨璇勫鍏宠仈鎴愬姛: 姣旇禌ID={}, 璇勫ID={}, 闃舵ID=null", activityId, judgeInput.getJudgeId()); } else { // 涓烘瘡涓樁娈靛垱寤哄叧鑱� for (Activity stage : stages) { ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), stage.getId()); - activityJudgeRepository.save(activityJudge); + activityJudge = activityJudgeRepository.save(activityJudge); + log.info("淇濆瓨璇勫鍏宠仈鎴愬姛: 姣旇禌ID={}, 璇勫ID={}, 闃舵ID={}", activityId, judgeInput.getJudgeId(), stage.getId()); } } } else { @@ -303,7 +322,8 @@ // 濡傛灉stageId绛変簬褰撳墠姣旇禌ID锛岃〃绀鸿瘎濮旇礋璐f暣涓瘮璧涳紝stage_id璁句负null Long actualStageId = stageId.equals(activityId) ? null : stageId; ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), actualStageId); - activityJudgeRepository.save(activityJudge); + activityJudge = activityJudgeRepository.save(activityJudge); + log.info("淇濆瓨璇勫鍏宠仈鎴愬姛: 姣旇禌ID={}, 璇勫ID={}, 闃舵ID={}", activityId, judgeInput.getJudgeId(), actualStageId); } } } diff --git a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java index 03f19a7..022a785 100644 --- a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java +++ b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java @@ -23,11 +23,18 @@ @SuppressWarnings("unchecked") public PageResponse<ActivityPlayerApplicationResponse> listApplications(String name, Long activityId, Integer state, Integer page, Integer size) { String baseSql = - "SELECT ap.id, CONCAT(p.name, '锛�', ap.project_name, '锛�') AS player_name, parent.name AS activity_name, ap.project_name AS project_name, p.phone AS phone, ap.create_time AS apply_time, ap.state AS state " + + "SELECT ap.id, p.name AS player_name, parent.name AS activity_name, ap.project_name AS project_name, p.phone AS phone, ap.create_time AS apply_time, ap.state AS state, " + + "COALESCE(rating_stats.rating_count, 0) AS rating_count, rating_stats.average_score " + "FROM t_activity_player ap " + "JOIN t_player p ON p.id = ap.player_id " + "JOIN t_activity stage ON stage.id = ap.stage_id " + - "JOIN t_activity parent ON parent.id = stage.pid "; + "JOIN t_activity parent ON parent.id = stage.pid " + + "LEFT JOIN (" + + " SELECT activity_player_id, COUNT(*) AS rating_count, AVG(total_score) AS average_score " + + " FROM t_activity_player_rating " + + " WHERE state = 1 " + + " GROUP BY activity_player_id" + + ") rating_stats ON rating_stats.activity_player_id = ap.id "; StringBuilder whereClause = new StringBuilder(); boolean hasCondition = false; diff --git a/fix-roles.ps1 b/fix-roles.ps1 new file mode 100644 index 0000000..e301f01 --- /dev/null +++ b/fix-roles.ps1 @@ -0,0 +1,81 @@ +# Fix employee role codes +$API_URL = "http://localhost:8080/api/graphql" +$TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIyIiwicGhvbmUiOiIxMzk4MTk3MDgxNiIsImlhdCI6MTc1OTMyMzk3MSwiZXhwIjoxNzU5NDEwMzcxfQ.z8n5QuCpALluVJD9JShNsjQSaPqm91HcKV-5PB3jLN4" + +$headers = @{ + "Content-Type" = "application/json" + "Authorization" = "Bearer $TOKEN" +} + +Write-Host "Getting employees..." +$employeesQuery = '{"query":"query { employees { id name phone roleId description } }"}' +$employeesResponse = Invoke-RestMethod -Uri $API_URL -Method POST -Headers $headers -Body $employeesQuery +$employees = $employeesResponse.data.employees + +Write-Host "Found $($employees.Count) employees" + +Write-Host "Getting valid roles..." +$rolesQuery = '{"query":"query { activeRoles { id code name description } }"}' +$rolesResponse = Invoke-RestMethod -Uri $API_URL -Method POST -Headers $headers -Body $rolesQuery +$validRoles = $rolesResponse.data.activeRoles + +$validRoleCodes = $validRoles | ForEach-Object { $_.code } +$invalidEmployees = $employees | Where-Object { $_.roleId -notin $validRoleCodes } + +if ($invalidEmployees.Count -eq 0) { + Write-Host "All employee role codes are valid!" + exit 0 +} + +Write-Host "Found $($invalidEmployees.Count) employees with invalid role codes" + +$roleMapping = @{ + "MANAGER" = "SUPER_ADMIN" + "EMPLOYEE" = "AUDITOR" + "ADMIN" = "SUPER_ADMIN" +} + +$fixedCount = 0 +$errorCount = 0 + +foreach ($emp in $invalidEmployees) { + $newRoleId = $roleMapping[$emp.roleId] + if (-not $newRoleId) { + $newRoleId = "AUDITOR" + } + + Write-Host "Fixing employee $($emp.name): $($emp.roleId) -> $newRoleId" + + $mutationBody = @{ + query = 'mutation SaveEmployee($input: EmployeeInput!) { saveEmployee(input: $input) { id name roleId } }' + variables = @{ + input = @{ + id = [int]$emp.id + name = $emp.name + phone = $emp.phone + roleId = $newRoleId + description = $emp.description + } + } + } + + $mutation = $mutationBody | ConvertTo-Json -Depth 10 + + try { + $result = Invoke-RestMethod -Uri $API_URL -Method POST -Headers $headers -Body $mutation + if ($result.errors) { + Write-Host " Error: $($result.errors[0].message)" -ForegroundColor Red + $errorCount++ + } else { + Write-Host " Success!" -ForegroundColor Green + $fixedCount++ + } + } catch { + Write-Host " Exception: $($_.Exception.Message)" -ForegroundColor Red + $errorCount++ + } +} + +Write-Host "Fix completed!" +Write-Host "Successfully fixed: $fixedCount employees" +Write-Host "Failed to fix: $errorCount employees" \ No newline at end of file diff --git a/fix_employee_roles.sql b/fix_employee_roles.sql new file mode 100644 index 0000000..5cd23ba --- /dev/null +++ b/fix_employee_roles.sql @@ -0,0 +1,27 @@ +-- 淇鍛樺伐琛ㄤ腑鐨勬棤鏁堣鑹蹭唬鐮� +-- 灏嗘棤鏁堢殑瑙掕壊浠g爜鏇挎崲涓烘湁鏁堢殑瑙掕壊浠g爜 + +-- 鏌ョ湅褰撳墠鍛樺伐琛ㄤ腑鐨勮鑹蹭唬鐮佸垎甯� +SELECT role_id, COUNT(*) as count FROM t_employee WHERE state = 1 GROUP BY role_id; + +-- 鏌ョ湅鏈夋晥鐨勮鑹蹭唬鐮� +SELECT code, name FROM t_role WHERE state = 1; + +-- 淇鏃犳晥鐨勮鑹蹭唬鐮� +-- MANAGER -> SUPER_ADMIN (绠$悊鍛�) +UPDATE t_employee SET role_id = 'SUPER_ADMIN' WHERE role_id = 'MANAGER' AND state = 1; + +-- EMPLOYEE -> AUDITOR (骞冲彴宸ヤ綔浜哄憳) +UPDATE t_employee SET role_id = 'AUDITOR' WHERE role_id = 'EMPLOYEE' AND state = 1; + +-- ADMIN -> SUPER_ADMIN (瓒呯骇绠$悊鍛�) +UPDATE t_employee SET role_id = 'SUPER_ADMIN' WHERE role_id = 'ADMIN' AND state = 1; + +-- 楠岃瘉淇缁撴灉 +SELECT role_id, COUNT(*) as count FROM t_employee WHERE state = 1 GROUP BY role_id; + +-- 妫�鏌ユ槸鍚﹁繕鏈夋棤鏁堢殑瑙掕壊浠g爜 +SELECT e.id, e.name, e.role_id +FROM t_employee e +LEFT JOIN t_role r ON e.role_id = r.code AND r.state = 1 +WHERE e.state = 1 AND r.code IS NULL; \ No newline at end of file diff --git a/web/check-auth.html b/web/check-auth.html new file mode 100644 index 0000000..81bdd4c --- /dev/null +++ b/web/check-auth.html @@ -0,0 +1,128 @@ +<!DOCTYPE html> +<html lang="zh-CN"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>璁よ瘉鐘舵�佹鏌�</title> + <style> + body { font-family: Arial, sans-serif; margin: 20px; } + .result { margin: 10px 0; padding: 10px; border: 1px solid #ccc; } + .error { background-color: #ffe6e6; } + .success { background-color: #e6ffe6; } + button { padding: 10px 20px; margin: 5px; } + </style> +</head> +<body> + <h1>璁よ瘉鐘舵�佹鏌�</h1> + + <button onclick="checkAuth()">妫�鏌ヨ璇佺姸鎬�</button> + <button onclick="testRoleAPI()">娴嬭瘯瑙掕壊API</button> + <button onclick="loginAsAdmin()">妯℃嫙绠$悊鍛樼櫥褰�</button> + + <div id="results"></div> + + <script> + function addResult(content, isError = false) { + const div = document.createElement('div'); + div.className = `result ${isError ? 'error' : 'success'}`; + div.innerHTML = content; + document.getElementById('results').appendChild(div); + } + + function checkAuth() { + const token = localStorage.getItem('auth_token'); + const userInfo = localStorage.getItem('user_info'); + + addResult(` + <strong>璁よ瘉鐘舵��:</strong><br> + Token: ${token ? '瀛樺湪' : '涓嶅瓨鍦�'}<br> + User Info: ${userInfo ? '瀛樺湪' : '涓嶅瓨鍦�'}<br> + ${token ? `Token鍐呭: ${token.substring(0, 50)}...` : ''} + `); + } + + async function testRoleAPI() { + try { + const token = localStorage.getItem('auth_token'); + const headers = { + 'Content-Type': 'application/json' + }; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + const response = await fetch('/api/graphql', { + method: 'POST', + headers, + body: JSON.stringify({ + query: `query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + }` + }) + }); + + const data = await response.json(); + + if (data.errors) { + addResult(`<strong>瑙掕壊API閿欒:</strong><br>${JSON.stringify(data.errors, null, 2)}`, true); + } else { + addResult(`<strong>瑙掕壊API鎴愬姛:</strong><br>瑙掕壊鏁伴噺: ${data.data?.activeRoles?.length || 0}<br>鏁版嵁: <pre>${JSON.stringify(data.data, null, 2)}</pre>`); + } + } catch (error) { + addResult(`<strong>瑙掕壊API璋冪敤澶辫触:</strong><br>${error.message}`, true); + } + } + + async function loginAsAdmin() { + try { + const response = await fetch('/api/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: `mutation Login($phone: String!, $password: String!) { + login(phone: $phone, password: $password) { + token + user { + userId + name + phone + userType + } + } + }`, + variables: { + phone: "13800000001", + password: "123456" + } + }) + }); + + const data = await response.json(); + + if (data.errors) { + addResult(`<strong>鐧诲綍澶辫触:</strong><br>${JSON.stringify(data.errors, null, 2)}`, true); + } else if (data.data?.login?.token) { + localStorage.setItem('auth_token', data.data.login.token); + localStorage.setItem('user_info', JSON.stringify(data.data.login.user)); + addResult(`<strong>鐧诲綍鎴愬姛:</strong><br>Token宸蹭繚瀛�<br>鐢ㄦ埛淇℃伅: ${JSON.stringify(data.data.login.user, null, 2)}`); + } else { + addResult(`<strong>鐧诲綍鍝嶅簲寮傚父:</strong><br>${JSON.stringify(data, null, 2)}`, true); + } + } catch (error) { + addResult(`<strong>鐧诲綍璇锋眰澶辫触:</strong><br>${error.message}`, true); + } + } + </script> +</body> +</html> \ No newline at end of file diff --git a/web/debug-roles.html b/web/debug-roles.html new file mode 100644 index 0000000..fc03ed2 --- /dev/null +++ b/web/debug-roles.html @@ -0,0 +1,99 @@ +<!DOCTYPE html> +<html lang="zh-CN"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>瑙掕壊API璋冭瘯</title> + <style> + body { font-family: Arial, sans-serif; margin: 20px; } + .result { margin: 10px 0; padding: 10px; border: 1px solid #ccc; } + .error { background-color: #ffe6e6; } + .success { background-color: #e6ffe6; } + button { padding: 10px 20px; margin: 5px; } + </style> +</head> +<body> + <h1>瑙掕壊API璋冭瘯</h1> + + <button onclick="testWithoutAuth()">娴嬭瘯鏃犺璇丄PI璋冪敤</button> + <button onclick="testWithAuth()">娴嬭瘯甯﹁璇丄PI璋冪敤</button> + <button onclick="checkLocalStorage()">妫�鏌ユ湰鍦板瓨鍌�</button> + + <div id="results"></div> + + <script> + function addResult(content, isError = false) { + const div = document.createElement('div'); + div.className = `result ${isError ? 'error' : 'success'}`; + div.innerHTML = content; + document.getElementById('results').appendChild(div); + } + + async function testWithoutAuth() { + try { + const response = await fetch('/api/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: `query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + }` + }) + }); + + const data = await response.json(); + addResult(`<strong>鏃犺璇丄PI璋冪敤缁撴灉:</strong><br>鐘舵��: ${response.status}<br>鏁版嵁: <pre>${JSON.stringify(data, null, 2)}</pre>`); + } catch (error) { + addResult(`<strong>鏃犺璇丄PI璋冪敤澶辫触:</strong><br>${error.message}`, true); + } + } + + async function testWithAuth() { + try { + const token = localStorage.getItem('token'); + const response = await fetch('/api/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': token ? `Bearer ${token}` : '' + }, + body: JSON.stringify({ + query: `query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + }` + }) + }); + + const data = await response.json(); + addResult(`<strong>甯﹁璇丄PI璋冪敤缁撴灉:</strong><br>鐘舵��: ${response.status}<br>Token: ${token ? '瀛樺湪' : '涓嶅瓨鍦�'}<br>鏁版嵁: <pre>${JSON.stringify(data, null, 2)}</pre>`); + } catch (error) { + addResult(`<strong>甯﹁璇丄PI璋冪敤澶辫触:</strong><br>${error.message}`, true); + } + } + + function checkLocalStorage() { + const token = localStorage.getItem('token'); + const user = localStorage.getItem('user'); + addResult(`<strong>鏈湴瀛樺偍妫�鏌�:</strong><br>Token: ${token || '鏃�'}<br>User: ${user || '鏃�'}`); + } + </script> +</body> +</html> \ No newline at end of file diff --git a/web/fix-employee-roles.html b/web/fix-employee-roles.html new file mode 100644 index 0000000..26131a6 --- /dev/null +++ b/web/fix-employee-roles.html @@ -0,0 +1,307 @@ +<!DOCTYPE html> +<html lang="zh-CN"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>淇鍛樺伐瑙掕壊浠g爜</title> + <style> + body { + font-family: Arial, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + } + .section { + margin: 20px 0; + padding: 15px; + border: 1px solid #ddd; + border-radius: 5px; + } + button { + background-color: #007bff; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + margin: 5px; + } + button:hover { + background-color: #0056b3; + } + .error { + color: red; + } + .success { + color: green; + } + .log { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 10px; + margin: 10px 0; + white-space: pre-wrap; + font-family: monospace; + max-height: 300px; + overflow-y: auto; + } + </style> +</head> +<body> + <h1>淇鍛樺伐瑙掕壊浠g爜</h1> + + <div class="section"> + <h3>1. 妫�鏌ヨ璇佺姸鎬�</h3> + <button onclick="checkAuth()">妫�鏌ヨ璇佺姸鎬�</button> + <div id="authStatus"></div> + </div> + + <div class="section"> + <h3>2. 鑾峰彇鎵�鏈夊憳宸�</h3> + <button onclick="fetchEmployees()">鑾峰彇鍛樺伐鍒楄〃</button> + <div id="employeeList"></div> + </div> + + <div class="section"> + <h3>3. 鑾峰彇鏈夋晥瑙掕壊</h3> + <button onclick="fetchRoles()">鑾峰彇瑙掕壊鍒楄〃</button> + <div id="roleList"></div> + </div> + + <div class="section"> + <h3>4. 淇鏃犳晥瑙掕壊</h3> + <button onclick="fixInvalidRoles()">淇鏃犳晥瑙掕壊浠g爜</button> + <div id="fixResult"></div> + </div> + + <div class="section"> + <h3>鎿嶄綔鏃ュ織</h3> + <div id="log" class="log"></div> + <button onclick="clearLog()">娓呯┖鏃ュ織</button> + </div> + + <script> + const API_BASE_URL = 'http://localhost:8080/api'; + const GRAPHQL_ENDPOINT = `${API_BASE_URL}/graphql`; + + let employees = []; + let validRoles = []; + + function log(message) { + const logDiv = document.getElementById('log'); + const timestamp = new Date().toLocaleTimeString(); + logDiv.textContent += `[${timestamp}] ${message}\n`; + logDiv.scrollTop = logDiv.scrollHeight; + } + + function clearLog() { + document.getElementById('log').textContent = ''; + } + + async function graphqlRequest(query, variables = {}) { + const token = localStorage.getItem('token'); + + const response = await fetch(GRAPHQL_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token && { 'Authorization': `Bearer ${token}` }) + }, + body: JSON.stringify({ + query, + variables + }) + }); + + const result = await response.json(); + + if (result.errors) { + throw new Error(result.errors[0].message); + } + + return result.data; + } + + async function checkAuth() { + const statusDiv = document.getElementById('authStatus'); + const token = localStorage.getItem('token'); + + if (!token) { + statusDiv.innerHTML = '<span class="error">鏈壘鍒拌璇佷护鐗岋紝璇峰厛鐧诲綍</span>'; + log('閿欒: 鏈壘鍒拌璇佷护鐗�'); + return; + } + + try { + // 灏濊瘯鑾峰彇鐢ㄦ埛淇℃伅鏉ラ獙璇乼oken + const query = ` + query { + employees { + id + name + } + } + `; + + await graphqlRequest(query); + statusDiv.innerHTML = '<span class="success">璁よ瘉鐘舵�佹甯�</span>'; + log('璁よ瘉鐘舵�佹鏌ラ�氳繃'); + } catch (error) { + statusDiv.innerHTML = `<span class="error">璁よ瘉澶辫触: ${error.message}</span>`; + log(`璁よ瘉澶辫触: ${error.message}`); + } + } + + async function fetchEmployees() { + const listDiv = document.getElementById('employeeList'); + + try { + const query = ` + query { + employees { + id + name + phone + roleId + description + } + } + `; + + const data = await graphqlRequest(query); + employees = data.employees; + + let html = `<p>鎵惧埌 ${employees.length} 涓憳宸�:</p><ul>`; + employees.forEach(emp => { + html += `<li>ID: ${emp.id}, 濮撳悕: ${emp.name}, 瑙掕壊: ${emp.roleId}</li>`; + }); + html += '</ul>'; + + listDiv.innerHTML = html; + log(`鎴愬姛鑾峰彇 ${employees.length} 涓憳宸); + } catch (error) { + listDiv.innerHTML = `<span class="error">鑾峰彇鍛樺伐澶辫触: ${error.message}</span>`; + log(`鑾峰彇鍛樺伐澶辫触: ${error.message}`); + } + } + + async function fetchRoles() { + const listDiv = document.getElementById('roleList'); + + try { + const query = ` + query { + activeRoles { + id + code + name + description + } + } + `; + + const data = await graphqlRequest(query); + validRoles = data.activeRoles; + + let html = `<p>鎵惧埌 ${validRoles.length} 涓湁鏁堣鑹�:</p><ul>`; + validRoles.forEach(role => { + html += `<li>浠g爜: ${role.code}, 鍚嶇О: ${role.name}</li>`; + }); + html += '</ul>'; + + listDiv.innerHTML = html; + log(`鎴愬姛鑾峰彇 ${validRoles.length} 涓湁鏁堣鑹瞏); + } catch (error) { + listDiv.innerHTML = `<span class="error">鑾峰彇瑙掕壊澶辫触: ${error.message}</span>`; + log(`鑾峰彇瑙掕壊澶辫触: ${error.message}`); + } + } + + async function fixInvalidRoles() { + const resultDiv = document.getElementById('fixResult'); + + if (employees.length === 0) { + resultDiv.innerHTML = '<span class="error">璇峰厛鑾峰彇鍛樺伐鍒楄〃</span>'; + return; + } + + if (validRoles.length === 0) { + resultDiv.innerHTML = '<span class="error">璇峰厛鑾峰彇瑙掕壊鍒楄〃</span>'; + return; + } + + const validRoleCodes = validRoles.map(role => role.code); + const invalidEmployees = employees.filter(emp => !validRoleCodes.includes(emp.roleId)); + + if (invalidEmployees.length === 0) { + resultDiv.innerHTML = '<span class="success">鎵�鏈夊憳宸ョ殑瑙掕壊浠g爜閮芥槸鏈夋晥鐨�</span>'; + log('鎵�鏈夊憳宸ョ殑瑙掕壊浠g爜閮芥槸鏈夋晥鐨�'); + return; + } + + log(`鍙戠幇 ${invalidEmployees.length} 涓憳宸ョ殑瑙掕壊浠g爜鏃犳晥`); + + // 瑙掕壊鏄犲皠瑙勫垯 + const roleMapping = { + 'MANAGER': 'SUPER_ADMIN', + 'EMPLOYEE': 'AUDITOR', + 'ADMIN': 'SUPER_ADMIN' + }; + + let fixedCount = 0; + let errors = []; + + for (const emp of invalidEmployees) { + const newRoleId = roleMapping[emp.roleId] || 'AUDITOR'; // 榛樿鏄犲皠鍒癆UDITOR + + try { + const mutation = ` + mutation SaveEmployee($input: EmployeeInput!) { + saveEmployee(input: $input) { + id + name + roleId + } + } + `; + + const variables = { + input: { + id: parseInt(emp.id), + name: emp.name, + phone: emp.phone, + roleId: newRoleId, + description: emp.description + } + }; + + await graphqlRequest(mutation, variables); + fixedCount++; + log(`淇鍛樺伐 ${emp.name} (ID: ${emp.id}): ${emp.roleId} -> ${newRoleId}`); + } catch (error) { + errors.push(`鍛樺伐 ${emp.name} (ID: ${emp.id}): ${error.message}`); + log(`淇鍛樺伐 ${emp.name} 澶辫触: ${error.message}`); + } + } + + let html = `<p class="success">鎴愬姛淇 ${fixedCount} 涓憳宸ョ殑瑙掕壊浠g爜</p>`; + if (errors.length > 0) { + html += `<p class="error">淇澶辫触 ${errors.length} 涓�:</p><ul>`; + errors.forEach(error => { + html += `<li>${error}</li>`; + }); + html += '</ul>'; + } + + resultDiv.innerHTML = html; + log(`淇瀹屾垚: 鎴愬姛 ${fixedCount} 涓�, 澶辫触 ${errors.length} 涓猔); + } + + // 椤甸潰鍔犺浇鏃舵鏌ヨ璇佺姸鎬� + window.onload = function() { + checkAuth(); + }; + </script> +</body> +</html> \ No newline at end of file diff --git a/web/src/api/activity.js b/web/src/api/activity.js index bd78df0..c46c220 100644 --- a/web/src/api/activity.js +++ b/web/src/api/activity.js @@ -122,8 +122,8 @@ // API 鍑芥暟 export const getActivities = async (page = 0, size = 10, name = '') => { try { - const data = await graphqlRequest(GET_ACTIVITIES_QUERY, { page, size, name }); - return data.activities; + const result = await graphqlRequest(GET_ACTIVITIES_QUERY, { page, size, name }); + return result.data.activities; } catch (error) { throw new Error(error.message || '鑾峰彇姣旇禌鍒楄〃澶辫触'); } @@ -131,8 +131,8 @@ export const getActivity = async (id) => { try { - const data = await graphqlRequest(GET_ACTIVITY_QUERY, { id }); - return data.activity; + const result = await graphqlRequest(GET_ACTIVITY_QUERY, { id }); + return result.data.activity; } catch (error) { throw new Error(error.message || '鑾峰彇姣旇禌璇︽儏澶辫触'); } @@ -140,14 +140,9 @@ export const getAllActivities = async () => { try { - console.log('=== getAllActivities API璋冪敤寮�濮� ==='); - console.log('GraphQL鏌ヨ:', GET_ALL_ACTIVITIES_QUERY); + const result = await graphqlRequest(GET_ALL_ACTIVITIES_QUERY); - const data = await graphqlRequest(GET_ALL_ACTIVITIES_QUERY); - console.log('GraphQL杩斿洖鐨勫師濮嬫暟鎹�:', data); - console.log('data.allActivities:', data.allActivities); - - return data.allActivities; + return result.data.allActivities; } catch (error) { console.error('=== getAllActivities API璋冪敤澶辫触 ==='); console.error('閿欒璇︽儏:', error); diff --git a/web/src/api/carousel.js b/web/src/api/carousel.js index 802b3f6..e28f364 100644 --- a/web/src/api/carousel.js +++ b/web/src/api/carousel.js @@ -77,26 +77,26 @@ export const CarouselApi = { // 鍒嗛〉鏌ヨ杞挱鍥� getCarousels: async (page = 0, size = 10, title) => { - const data = await graphqlRequest(GET_CAROUSELS, { page, size, title }) - return data.carousels + const result = await graphqlRequest(GET_CAROUSELS, { page, size, title }) + return result.data.carousels }, // 鏍规嵁ID鏌ヨ杞挱鍥� getCarousel: async (id) => { - const data = await graphqlRequest(GET_CAROUSEL, { id }) - return data.carousel + const result = await graphqlRequest(GET_CAROUSEL, { id }) + return result.data.carousel }, // 鑾峰彇鎾斁鍒楄〃 getPlayList: async () => { - const data = await graphqlRequest(GET_CAROUSEL_PLAY_LIST) - return data.carouselPlayList + const result = await graphqlRequest(GET_CAROUSEL_PLAY_LIST) + return result.data.carouselPlayList }, // 淇濆瓨杞挱鍥� saveCarousel: async (carousel) => { - const data = await graphqlRequest(SAVE_CAROUSEL, { carousel }) - return data.saveCarousel + const result = await graphqlRequest(SAVE_CAROUSEL, { carousel }) + return result.data.saveCarousel }, // 鍒犻櫎杞挱鍥� diff --git a/web/src/api/employee.ts b/web/src/api/employee.ts index 75c9dac..6cb318e 100644 --- a/web/src/api/employee.ts +++ b/web/src/api/employee.ts @@ -19,7 +19,7 @@ } `, - // 鏍规嵁濮撳悕鎼滅储鍛樺伐 + // 鎼滅储鍛樺伐 SEARCH_EMPLOYEES: ` query SearchEmployees($name: String) { employeesByName(name: $name) { @@ -99,18 +99,18 @@ // 鑾峰彇鍛樺伐鍒楄〃 async getEmployees(): Promise<Employee[]> { try { - const data = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEES) - return data?.employees || [] + const result = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEES) + return result?.data?.employees || [] } catch (error: any) { throw new Error(error.message || '鑾峰彇鍛樺伐鍒楄〃澶辫触') } }, // 鎼滅储鍛樺伐 - async searchEmployees(keyword: string): Promise<Employee[]> { + async searchEmployees(name: string): Promise<Employee[]> { try { - const data = await graphqlRequest(EMPLOYEE_QUERIES.SEARCH_EMPLOYEES, { keyword }) - return data?.searchEmployees || [] + const result = await graphqlRequest(EMPLOYEE_QUERIES.SEARCH_EMPLOYEES, { name }) + return result?.data?.employeesByName || [] } catch (error: any) { throw new Error(error.message || '鎼滅储鍛樺伐澶辫触') } @@ -119,8 +119,8 @@ // 鏍规嵁ID鑾峰彇鍛樺伐璇︽儏 async getEmployee(id: string): Promise<Employee | null> { try { - const data = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEE, { id }) - return data?.employee || null + const result = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEE, { id }) + return result?.data?.employee || null } catch (error: any) { throw new Error(error.message || '鑾峰彇鍛樺伐璇︽儏澶辫触') } @@ -129,8 +129,8 @@ // 淇濆瓨鍛樺伐锛堟柊澧炴垨鏇存柊锛� async saveEmployee(employee: EmployeeInput): Promise<Employee> { try { - const data = await graphqlRequest(EMPLOYEE_MUTATIONS.SAVE_EMPLOYEE, { input: employee }) - return data?.saveEmployee + const result = await graphqlRequest(EMPLOYEE_MUTATIONS.SAVE_EMPLOYEE, { input: employee }) + return result?.data?.saveEmployee } catch (error: any) { throw new Error(error.message || '淇濆瓨鍛樺伐澶辫触') } @@ -139,8 +139,8 @@ // 鍒犻櫎鍛樺伐 async deleteEmployee(id: string): Promise<boolean> { try { - const data = await graphqlRequest(EMPLOYEE_MUTATIONS.DELETE_EMPLOYEE, { id }) - return data?.deleteEmployee || false + const result = await graphqlRequest(EMPLOYEE_MUTATIONS.DELETE_EMPLOYEE, { id }) + return result?.data?.deleteEmployee || false } catch (error: any) { throw new Error(error.message || '鍒犻櫎鍛樺伐澶辫触') } diff --git a/web/src/api/media.js b/web/src/api/media.js index 6c7afdc..7bec028 100644 --- a/web/src/api/media.js +++ b/web/src/api/media.js @@ -86,38 +86,19 @@ }; export const deleteMedia = async (id) => { - console.log('=== deleteMedia API璋冪敤 ==='); - console.log('瑕佸垹闄ょ殑濯掍綋ID:', id); - console.log('GraphQL鏌ヨ:', DELETE_MEDIA_MUTATION); - - // 鑾峰彇JWT token - const { getToken } = await import('@/utils/auth'); - const token = getToken(); - const headers = { 'Content-Type': 'application/json' }; - if (token) { - headers['Authorization'] = `Bearer ${token}`; + try { + const variables = { id: parseInt(id) }; + + // 鍙戦�丟raphQL璇锋眰 + const result = await graphqlRequest(DELETE_MEDIA_MUTATION, variables); + + // 妫�鏌ヨ繑鍥炵粨鏋� + const deleteResult = result.data?.deleteMedia; + + return deleteResult; + } catch (error) { + throw new Error(`鍒犻櫎濯掍綋澶辫触: ${error.message}`); } - - const res = await fetch(GRAPHQL_ENDPOINT, { - method: 'POST', - headers: headers, - body: JSON.stringify({ - query: DELETE_MEDIA_MUTATION, - variables: { id: id.toString() } - }) - }); - const result = await res.json(); - console.log('GraphQL鍝嶅簲:', result); - console.log('deleteMedia缁撴灉:', result.data?.deleteMedia); - - if (result.errors) { - console.error('GraphQL閿欒:', result.errors); - throw new Error(result.errors[0].message); - } - - const deleteResult = result.data.deleteMedia; - console.log('杩斿洖鐨勫垹闄ょ粨鏋�:', deleteResult, '绫诲瀷:', typeof deleteResult); - return deleteResult; }; // 涓婁紶鏂囦欢鍒版湇鍔″櫒 @@ -152,17 +133,11 @@ const { extractVideoFrame, generateThumbnailFileName } = await import('@/utils/video.js'); try { - console.log('寮�濮嬪鐞嗚棰戞枃浠�:', videoFile.name); - - // 1. 涓婁紶鍘熻棰戞枃浠� - console.log('涓婁紶瑙嗛鏂囦欢...'); + // 1. 涓婁紶瑙嗛鏂囦欢 const videoUploadResult = await uploadFile(videoFile); - console.log('瑙嗛涓婁紶鎴愬姛:', videoUploadResult); // 2. 鎻愬彇瑙嗛绗竴甯� - console.log('鎻愬彇瑙嗛绗竴甯�...'); const thumbnailBlob = await extractVideoFrame(videoFile); - console.log('瑙嗛甯ф彁鍙栨垚鍔燂紝澶у皬:', thumbnailBlob.size); // 3. 鍒涘缓缂╃暐鍥炬枃浠跺璞� const thumbnailFileName = generateThumbnailFileName(videoFile.name); @@ -171,9 +146,7 @@ }); // 4. 涓婁紶缂╃暐鍥� - console.log('涓婁紶缂╃暐鍥�...'); const thumbnailUploadResult = await uploadFile(thumbnailFile); - console.log('缂╃暐鍥句笂浼犳垚鍔�:', thumbnailUploadResult); // 5. 杩斿洖鍖呭惈瑙嗛鍜岀缉鐣ュ浘淇℃伅鐨勭粨鏋� return { diff --git a/web/src/api/player.js b/web/src/api/player.js index c14cc6e..6d8e987 100644 --- a/web/src/api/player.js +++ b/web/src/api/player.js @@ -68,8 +68,8 @@ // API鍑芥暟 export const getPlayers = async (page = 0, size = 10, name = '') => { try { - const data = await graphqlRequest(GET_PLAYERS_QUERY, { page, size, name }) - return data.players + const result = await graphqlRequest(GET_PLAYERS_QUERY, { page, size, name }) + return result.data.players } catch (error) { throw new Error(error.message || '鑾峰彇瀛﹀憳鍒楄〃澶辫触') } @@ -77,8 +77,8 @@ export const getPlayer = async (id) => { try { - const data = await graphqlRequest(GET_PLAYER_QUERY, { id }) - return data.player + const result = await graphqlRequest(GET_PLAYER_QUERY, { id }) + return result.data.player } catch (error) { throw new Error(error.message || '鑾峰彇瀛﹀憳璇︽儏澶辫触') } @@ -86,8 +86,8 @@ export const savePlayer = async (playerData) => { try { - const data = await graphqlRequest(SAVE_PLAYER_MUTATION, { input: playerData }) - return data.savePlayer + const result = await graphqlRequest(SAVE_PLAYER_MUTATION, { input: playerData }) + return result.data.savePlayer } catch (error) { throw new Error(error.message || '淇濆瓨瀛﹀憳澶辫触') } @@ -95,8 +95,8 @@ export const deletePlayer = async (id) => { try { - const data = await graphqlRequest(DELETE_PLAYER_MUTATION, { id }) - return data.deletePlayer + const result = await graphqlRequest(DELETE_PLAYER_MUTATION, { id }) + return result.data.deletePlayer } catch (error) { throw new Error(error.message || '鍒犻櫎瀛﹀憳澶辫触') } @@ -117,7 +117,7 @@ export const PlayerApi = { getApplications: async (name, activityId, state, page, size) => { - const data = await graphqlRequest(GET_APPLICATIONS, { name, activityId, state, page, size }) - return data.activityPlayerApplications || { content: [], totalElements: 0, page: 1, size: 10 } + const result = await graphqlRequest(GET_APPLICATIONS, { name, activityId, state, page, size }) + return result.data.activityPlayerApplications || { content: [], totalElements: 0, page: 1, size: 10 } } } \ No newline at end of file diff --git a/web/src/api/projectReview.js b/web/src/api/projectReview.js index 37e9148..c668f3b 100644 --- a/web/src/api/projectReview.js +++ b/web/src/api/projectReview.js @@ -126,15 +126,17 @@ */ export const getActiveActivities = async () => { try { - const data = await graphqlRequest(GET_ACTIVITY_STAGES_QUERY) + console.log('=== 璋冪敤 getActiveActivities ===') + const result = await graphqlRequest(GET_ACTIVITY_STAGES_QUERY) + console.log('GraphQL 杩斿洖鏁版嵁:', result) // 鍚庣宸茬粡杩囨护浜嗘瘮璧涢樁娈碉紝鐩存帴杩斿洖 - const activities = data.allActivityStages || [] + const activities = result.data.allActivityStages || [] + console.log('澶勭悊鍚庣殑 activities:', activities) - console.log('鑾峰彇鍒扮殑姣旇禌闃舵:', activities) return activities } catch (error) { - console.error('鑾峰彇姣旇禌闃舵澶辫触:', error) + console.error('getActiveActivities 閿欒:', error) throw new Error(error.message || '鑾峰彇姣旇禌闃舵澶辫触') } } @@ -144,13 +146,13 @@ */ export const getCompetitionProjects = async (activityId, page = 0, size = 10) => { try { - const data = await graphqlRequest(GET_COMPETITION_PROJECTS_QUERY, { + const result = await graphqlRequest(GET_COMPETITION_PROJECTS_QUERY, { activityId, page, size }) - const projects = data.activityPlayerApplications || [] + const projects = result.data.activityPlayerApplications || [] // 涓烘瘡涓」鐩幏鍙栬瘎鍒嗙粺璁� const enrichedProjects = await Promise.all( @@ -207,8 +209,8 @@ */ export const getProjectDetail = async (id) => { try { - const data = await graphqlRequest(GET_PROJECT_DETAIL_QUERY, { id }) - return data.activityPlayerDetail + const result = await graphqlRequest(GET_PROJECT_DETAIL_QUERY, { id }) + return result.data.activityPlayerDetail } catch (error) { throw new Error(error.message || '鑾峰彇椤圭洰璇︽儏澶辫触') } @@ -219,10 +221,10 @@ */ export const getRatingStats = async (activityPlayerId) => { try { - const data = await graphqlRequest(GET_RATING_STATS_QUERY, { activityPlayerId }) + const result = await graphqlRequest(GET_RATING_STATS_QUERY, { activityPlayerId }) - const ratings = data.judgeRatingsForPlayer || [] - const averageScore = data.averageScoreForPlayer || 0 + const ratings = result.data.judgeRatingsForPlayer || [] + const averageScore = result.data.averageScoreForPlayer || 0 // 鍙绠楀凡璇勫垎鐨勮瘎濮� const ratedJudges = ratings.filter(rating => rating.hasRated) @@ -248,8 +250,8 @@ */ export const getCurrentJudgeRating = async (activityPlayerId) => { try { - const data = await graphqlRequest(GET_CURRENT_JUDGE_RATING_QUERY, { activityPlayerId }) - return data.currentJudgeRating + const result = await graphqlRequest(GET_CURRENT_JUDGE_RATING_QUERY, { activityPlayerId }) + return result.data.currentJudgeRating } catch (error) { throw new Error(error.message || '鑾峰彇褰撳墠璇勫璇勫垎澶辫触') } @@ -260,8 +262,8 @@ */ export const submitRating = async (ratingInput) => { try { - const data = await graphqlRequest(SUBMIT_RATING_MUTATION, { input: ratingInput }) - return data.saveActivityPlayerRating + const result = await graphqlRequest(SUBMIT_RATING_MUTATION, { input: ratingInput }) + return result.data.saveActivityPlayerRating } catch (error) { throw new Error(error.message || '鎻愪氦璇勫垎澶辫触') } diff --git a/web/src/api/projectReviewNew.js b/web/src/api/projectReviewNew.js index 162d10d..5a2a8c7 100644 --- a/web/src/api/projectReviewNew.js +++ b/web/src/api/projectReviewNew.js @@ -23,8 +23,9 @@ ` // 鑾峰彇椤圭洰璇勫鍒楄〃 -export const getProjectReviewApplications = (params) => { - return graphqlRequest(GET_PROJECT_REVIEW_APPLICATIONS_QUERY, params) +export const getProjectReviewApplications = async (params) => { + const result = await graphqlRequest(GET_PROJECT_REVIEW_APPLICATIONS_QUERY, params) + return result.data } export default { diff --git a/web/src/api/promotion.js b/web/src/api/promotion.js index 6094476..87b678c 100644 --- a/web/src/api/promotion.js +++ b/web/src/api/promotion.js @@ -78,8 +78,8 @@ page: params.page || 1, size: params.size || 10 } - const data = await graphqlRequest(GET_PROMOTION_COMPETITIONS, variables) - return data.promotionCompetitions || [] + const result = await graphqlRequest(GET_PROMOTION_COMPETITIONS, variables) + return result.data.promotionCompetitions || [] } catch (error) { console.error('鑾峰彇姣旇禌鏅嬬骇鍒楄〃澶辫触:', error) throw error @@ -94,8 +94,8 @@ page: params.page || 1, size: params.size || 10 } - const data = await graphqlRequest(GET_COMPETITION_PARTICIPANTS, variables) - return data.competitionParticipants || [] + const result = await graphqlRequest(GET_COMPETITION_PARTICIPANTS, variables) + return result.data.competitionParticipants || [] } catch (error) { console.error('鑾峰彇姣旇禌鍙傝禌浜哄憳澶辫触:', error) throw error diff --git a/web/src/api/rating.js b/web/src/api/rating.js index 542ca97..cbe6bab 100644 --- a/web/src/api/rating.js +++ b/web/src/api/rating.js @@ -81,8 +81,8 @@ // API鍑芥暟 export const getRatingSchemes = async (page = 0, size = 10, name = '') => { try { - const data = await graphqlRequest(GET_RATING_SCHEMES_QUERY, { page, size, name }) - return data.ratingSchemes + const result = await graphqlRequest(GET_RATING_SCHEMES_QUERY, { page, size, name }) + return result.data.ratingSchemes } catch (error) { throw new Error(error.message || '鑾峰彇璇勫垎鏂规鍒楄〃澶辫触') } @@ -90,8 +90,8 @@ export const getAllRatingSchemes = async () => { try { - const data = await graphqlRequest(GET_ALL_RATING_SCHEMES_QUERY) - return data.allRatingSchemes || [] + const result = await graphqlRequest(GET_ALL_RATING_SCHEMES_QUERY) + return result.data.allRatingSchemes || [] } catch (error) { throw new Error(error.message || '鑾峰彇鎵�鏈夎瘎鍒嗘柟妗堝け璐�') } diff --git a/web/src/api/region.js b/web/src/api/region.js index 87cbcdf..d78274b 100644 --- a/web/src/api/region.js +++ b/web/src/api/region.js @@ -17,8 +17,8 @@ ` try { - const data = await graphqlRequest(query) - return data.regions || [] + const result = await graphqlRequest(query) + return result.data.regions || [] } catch (error) { throw new Error(error.message || '鑾峰彇鍦板尯鍒楄〃澶辫触') } @@ -39,8 +39,8 @@ ` try { - const data = await graphqlRequest(query, { id }) - return data.region + const result = await graphqlRequest(query, { id }) + return result.data.region } catch (error) { throw new Error(error.message || '鑾峰彇鍦板尯璇︽儏澶辫触') } @@ -98,8 +98,8 @@ ` try { - const data = await graphqlRequest(query) - return data.provinces || [] + const result = await graphqlRequest(query) + return result.data.provinces || [] } catch (error) { throw new Error(error.message || '鑾峰彇鐪佷唤鍒楄〃澶辫触') } @@ -119,8 +119,8 @@ ` try { - const data = await graphqlRequest(query, { provinceId }) - return data.cities || [] + const result = await graphqlRequest(query, { provinceId }) + return result.data.cities || [] } catch (error) { throw new Error(error.message || '鑾峰彇鍩庡競鍒楄〃澶辫触') } @@ -140,8 +140,8 @@ ` try { - const data = await graphqlRequest(query, { cityId }) - return data.districts || [] + const result = await graphqlRequest(query, { cityId }) + return result.data.districts || [] } catch (error) { throw new Error(error.message || '鑾峰彇鍖哄幙鍒楄〃澶辫触') } diff --git a/web/src/api/role.ts b/web/src/api/role.ts index 434ee33..441efb4 100644 --- a/web/src/api/role.ts +++ b/web/src/api/role.ts @@ -111,8 +111,8 @@ // 鑾峰彇鎵�鏈夎鑹� async getRoles(): Promise<Role[]> { try { - const data = await graphqlRequest(ROLE_QUERIES.GET_ROLES) - return data?.roles || [] + const result = await graphqlRequest(ROLE_QUERIES.GET_ROLES) + return result?.data?.roles || [] } catch (error: any) { throw new Error(error.message || '鑾峰彇瑙掕壊鍒楄〃澶辫触') } @@ -121,8 +121,8 @@ // 鑾峰彇婵�娲荤姸鎬佺殑瑙掕壊 async getActiveRoles(): Promise<Role[]> { try { - const data = await graphqlRequest(ROLE_QUERIES.GET_ACTIVE_ROLES) - return data?.activeRoles || [] + const result = await graphqlRequest(ROLE_QUERIES.GET_ACTIVE_ROLES) + return result?.data?.activeRoles || [] } catch (error: any) { throw new Error(error.message || '鑾峰彇婵�娲昏鑹插垪琛ㄥけ璐�') } @@ -131,8 +131,8 @@ // 鏍规嵁ID鑾峰彇瑙掕壊 async getRoleById(id: string): Promise<Role | null> { try { - const data = await graphqlRequest(ROLE_QUERIES.GET_ROLE, { id }) - return data?.role || null + const result = await graphqlRequest(ROLE_QUERIES.GET_ROLE, { id }) + return result?.data?.role || null } catch (error: any) { throw new Error(error.message || '鑾峰彇瑙掕壊璇︽儏澶辫触') } @@ -141,8 +141,8 @@ // 鏍规嵁浠g爜鑾峰彇瑙掕壊 async getRoleByCode(code: string): Promise<Role | null> { try { - const data = await graphqlRequest(ROLE_QUERIES.GET_ROLE_BY_CODE, { code }) - return data?.roleByCode || null + const result = await graphqlRequest(ROLE_QUERIES.GET_ROLE_BY_CODE, { code }) + return result?.data?.roleByCode || null } catch (error: any) { throw new Error(error.message || '鑾峰彇瑙掕壊璇︽儏澶辫触') } @@ -151,8 +151,8 @@ // 鏍规嵁鐘舵�佽幏鍙栬鑹� async getRolesByState(state: number): Promise<Role[]> { try { - const data = await graphqlRequest(ROLE_QUERIES.GET_ROLES_BY_STATE, { state }) - return data?.rolesByState || [] + const result = await graphqlRequest(ROLE_QUERIES.GET_ROLES_BY_STATE, { state }) + return result?.data?.rolesByState || [] } catch (error: any) { throw new Error(error.message || '鑾峰彇瑙掕壊鍒楄〃澶辫触') } @@ -161,8 +161,8 @@ // 鏍规嵁鍚嶇О鎼滅储瑙掕壊 async searchRolesByName(name: string): Promise<Role[]> { try { - const data = await graphqlRequest(ROLE_QUERIES.SEARCH_ROLES_BY_NAME, { name }) - return data?.searchRolesByName || [] + const result = await graphqlRequest(ROLE_QUERIES.SEARCH_ROLES_BY_NAME, { name }) + return result?.data?.searchRolesByName || [] } catch (error: any) { throw new Error(error.message || '鎼滅储瑙掕壊澶辫触') } diff --git a/web/src/components/JudgeFormSimple.vue b/web/src/components/JudgeFormSimple.vue index 9c335e2..8da6855 100644 --- a/web/src/components/JudgeFormSimple.vue +++ b/web/src/components/JudgeFormSimple.vue @@ -233,8 +233,6 @@ // 鐩戝惉璇勫鏁版嵁鍙樺寲锛屽~鍏呰〃鍗� watch(() => props.judgeData, (data) => { - console.log('馃攳 Watch triggered, judgeData:', data) - console.log('馃攳 isEdit computed:', isEdit.value) nextTick(async () => { if (data && data.id) { // 缂栬緫妯″紡锛氬~鍏呰〃鍗曟暟鎹� diff --git a/web/src/config/api.ts b/web/src/config/api.ts index 62cf341..c1430be 100644 --- a/web/src/config/api.ts +++ b/web/src/config/api.ts @@ -17,54 +17,44 @@ // GraphQL璇锋眰宸ュ叿鍑芥暟 export const graphqlRequest = async (query: string, variables: any = {}) => { - console.log('=== GraphQL璇锋眰寮�濮� ==='); - console.log('璇锋眰绔偣:', API_CONFIG.GRAPHQL_ENDPOINT); - console.log('鏌ヨ璇彞:', query); - console.log('鍙橀噺:', variables); - // 鑾峰彇JWT token const { getToken } = await import('@/utils/auth'); const token = getToken(); - console.log('JWT Token:', token ? '宸茶幏鍙�' : '鏈幏鍙�'); - + + // 鏋勫缓璇锋眰澶� const headers: Record<string, string> = { 'Content-Type': 'application/json', }; + if (token) { headers['Authorization'] = `Bearer ${token}`; } - console.log('璇锋眰澶�:', headers); + // 鏋勫缓璇锋眰浣� const requestBody = JSON.stringify({ query, variables, }); - console.log('璇锋眰浣�:', requestBody); try { + // 鍙戦�佽姹� const response = await fetch(API_CONFIG.GRAPHQL_ENDPOINT, { method: 'POST', - headers: headers, + headers, body: requestBody, - }) - - console.log('鍝嶅簲鐘舵��:', response.status); - console.log('鍝嶅簲鐘舵�佹枃鏈�:', response.statusText); + }); if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) + throw new Error(`HTTP error! status: ${response.status}`); } - const result = await response.json() - console.log('鍝嶅簲缁撴灉:', result); - + const result = await response.json(); + if (result.errors) { - console.error('GraphQL閿欒:', result.errors); - throw new Error(result.errors[0].message) + throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`); } - console.log('杩斿洖鏁版嵁:', result.data); - return result.data + return result; } catch (error) { console.error('=== GraphQL璇锋眰澶辫触 ==='); console.error('閿欒璇︽儏:', error); diff --git a/web/src/router/index.ts b/web/src/router/index.ts index f590f3d..617baaf 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -17,14 +17,8 @@ { path: '/activity', name: 'Activity', - component: () => import('@/views/activity/index.vue'), + component: () => import('@/views/activity-list.vue'), meta: { title: '姣旇禌绠$悊', icon: 'Trophy' } - }, - { - path: '/activity/:id', - name: 'ActivityDetail', - component: () => import('@/views/ActivityDetail.vue'), - meta: { title: '姣旇禌璇︽儏', icon: 'Trophy' } }, { path: '/activity/new', @@ -39,40 +33,46 @@ meta: { title: '缂栬緫姣旇禌', icon: 'Trophy' } }, { + path: '/activity/:id', + name: 'ActivityDetail', + component: () => import('@/views/ActivityDetail.vue'), + meta: { title: '姣旇禌璇︽儏', icon: 'Trophy' } + }, + { path: '/judge', name: 'Judge', - component: () => import('@/views/judge/index.vue'), + component: () => import('@/views/judge-list.vue'), meta: { title: '璇勫绠$悊', icon: 'UserFilled' } }, { path: '/rating-scheme', name: 'RatingScheme', - component: () => import('@/views/rating/index.vue'), - meta: { title: '璇勫垎妯℃澘', icon: 'Score' } + component: () => import('@/views/rating-list.vue'), + meta: { title: '璇勫垎妯℃澘', icon: 'Document' } }, { path: '/rating-scheme/new', name: 'RatingSchemeCreate', - component: () => import('@/views/rating/Form.vue'), - meta: { title: '鏂板璇勫垎妯℃澘', icon: 'Score' } + component: () => import('@/views/rating-detail.vue'), + meta: { title: '鏂板缓璇勫垎妯℃澘', hidden: true } }, { path: '/rating-scheme/edit/:id', name: 'RatingSchemeEdit', - component: () => import('@/views/rating/Form.vue'), - meta: { title: '缂栬緫璇勫垎妯℃澘', icon: 'Score' } + component: () => import('@/views/rating-detail.vue'), + meta: { title: '缂栬緫璇勫垎妯℃澘', hidden: true } }, { path: '/player', name: 'Player', - component: () => import('@/views/player/index.vue'), - meta: { title: '鎶ュ悕瀹℃牳', icon: 'UserFilled' } + component: () => import('@/views/check-list.vue'), + meta: { title: '鍙傝禌浜哄憳', icon: 'UserFilled' } }, { path: '/player/:id/detail', name: 'PlayerDetail', - component: () => import('@/views/player/detail.vue'), - meta: { title: '鎶ュ悕璇︽儏', icon: 'UserFilled' } + component: () => import('@/views/check-detail.vue'), + meta: { title: '鍙傝禌浜哄憳璇︽儏' } }, { path: '/activity-player/:id/rating', @@ -95,37 +95,37 @@ { path: '/employee', name: 'Employee', - component: () => import('@/views/employee/index.vue'), - meta: { title: '鍛樺伐绠$悊', icon: 'Avatar' } + component: () => import('@/views/employee-list.vue'), + meta: { title: '鍛樺伐绠$悊', icon: 'User' } }, { path: '/project-review', name: 'ProjectReview', - component: () => import('@/views/project-review/index.vue'), + component: () => import('@/views/review-list.vue'), meta: { title: '椤圭洰璇勫', icon: 'View' } }, { path: '/project-review/:id/detail', name: 'ProjectReviewDetail', - component: () => import('@/views/project-review/detail.vue'), + component: () => import('@/views/review-detail.vue'), meta: { title: '椤圭洰璇勫璇︽儏', hidden: true } }, { path: '/review', name: 'Review', - component: () => import('@/views/review/index.vue'), - meta: { title: '椤圭洰璇勫', icon: 'Edit' } + component: () => import('@/views/judge-review-list.vue'), + meta: { title: '璇勫璇勫', icon: 'Edit' } }, { path: '/review/:id/detail', name: 'ReviewDetail', - component: () => import('@/views/review/detail.vue'), - meta: { title: '椤圭洰璇勫璇︽儏', hidden: true } + component: () => import('@/views/judge-review-detail.vue'), + meta: { title: '璇勫璇勫璇︽儏', hidden: true } }, { path: '/competition-promotion', name: 'CompetitionPromotion', - component: () => import('@/views/competition-promotion/index.vue'), + component: () => import('@/views/next-list.vue'), meta: { title: '姣旇禌鏅嬬骇', icon: 'Promotion' } }, diff --git a/web/src/test/direct-upload-test.js b/web/src/test/direct-upload-test.js deleted file mode 100644 index e8d2af3..0000000 --- a/web/src/test/direct-upload-test.js +++ /dev/null @@ -1,84 +0,0 @@ -// 鐩存帴娴嬭瘯COS涓婁紶鍔熻兘 -async function testDirectUpload() { - try { - console.log('=== 寮�濮嬬洿鎺ヤ笂浼犳祴璇� ==='); - - // 1. 鑾峰彇涓婁紶鍑瘉 - console.log('1. 鑾峰彇涓婁紶鍑瘉...'); - const response = await fetch('http://localhost:8080/api/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - query GetUploadCredentials { - getUploadCredentials { - bucket - region - key - presignedUrl - expiration - } - } - ` - }) - }); - - const result = await response.json(); - console.log('GraphQL鍝嶅簲:', result); - - if (result.errors) { - throw new Error('GraphQL閿欒: ' + result.errors[0].message); - } - - const credentials = result.data.getUploadCredentials; - console.log('鑾峰彇鍒扮殑鍑瘉:', credentials); - - // 2. 鑾峰彇logo鏂囦欢 - console.log('2. 鑾峰彇logo鏂囦欢...'); - const logoResponse = await fetch('/UI/logo.jpg'); - if (!logoResponse.ok) { - throw new Error('鏃犳硶鑾峰彇logo鏂囦欢: ' + logoResponse.status); - } - - const logoBlob = await logoResponse.blob(); - console.log('Logo鏂囦欢淇℃伅:', { - size: logoBlob.size, - type: logoBlob.type - }); - - // 3. 涓婁紶鍒癈OS - console.log('3. 涓婁紶鍒癈OS...'); - console.log('涓婁紶URL:', credentials.presignedUrl); - - const uploadResponse = await fetch(credentials.presignedUrl, { - method: 'PUT', - body: logoBlob, - headers: { - 'Content-Type': logoBlob.type || 'image/jpeg', - } - }); - - console.log('涓婁紶鍝嶅簲鐘舵��:', uploadResponse.status); - console.log('涓婁紶鍝嶅簲澶�:', Object.fromEntries(uploadResponse.headers.entries())); - - if (uploadResponse.ok) { - const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}`; - console.log('鉁� 涓婁紶鎴愬姛!'); - console.log('鏂囦欢璁块棶URL:', fileUrl); - return fileUrl; - } else { - const errorText = await uploadResponse.text(); - console.error('鉂� 涓婁紶澶辫触:', uploadResponse.status, errorText); - throw new Error(`涓婁紶澶辫触: ${uploadResponse.status} - ${errorText}`); - } - - } catch (error) { - console.error('鉂� 娴嬭瘯澶辫触:', error); - throw error; - } -} - -// 鍦ㄦ祻瑙堝櫒鎺у埗鍙颁腑杩愯: testDirectUpload() -console.log('鐩存帴涓婁紶娴嬭瘯鍑芥暟宸插姞杞斤紝璇峰湪鎺у埗鍙拌繍琛�: testDirectUpload()'); \ No newline at end of file diff --git a/web/src/test/upload-logo-test.ts b/web/src/test/upload-logo-test.ts deleted file mode 100644 index d0399fa..0000000 --- a/web/src/test/upload-logo-test.ts +++ /dev/null @@ -1,140 +0,0 @@ -// Logo涓婁紶娴嬭瘯鑴氭湰 -import { uploadToCOS, getCOSConfig } from '../utils/cos' - -/** - * 娴嬭瘯涓婁紶Logo鏂囦欢鍒拌吘璁簯COS - */ -export const testUploadLogo = async () => { - console.log('=== 寮�濮婰ogo涓婁紶娴嬭瘯 ===') - - try { - // 1. 妫�鏌OS閰嶇疆 - console.log('姝ラ1: 鑾峰彇COS閰嶇疆淇℃伅...') - const config = await getCOSConfig() - console.log('COS閰嶇疆:', { - bucket: config.bucket, - region: config.region, - domain: config.domain - }) - - // 2. 鑾峰彇Logo鏂囦欢 - console.log('姝ラ2: 鑾峰彇Logo鏂囦欢...') - const logoFile = await fetchLogoFile() - - if (!logoFile) { - throw new Error('鏃犳硶鑾峰彇Logo鏂囦欢') - } - - console.log('Logo鏂囦欢淇℃伅:', { - name: logoFile.name, - size: logoFile.size, - type: logoFile.type - }) - - // 3. 涓婁紶鍒癈OS - console.log('姝ラ3: 涓婁紶Logo鍒癈OS...') - const uploadUrl = await uploadToCOS(logoFile, 'avatars/') - - console.log('鉁� Logo涓婁紶鎴愬姛!') - console.log('璁块棶URL:', uploadUrl) - - // 4. 楠岃瘉涓婁紶缁撴灉 - console.log('姝ラ4: 楠岃瘉涓婁紶缁撴灉...') - const isAccessible = await verifyFileAccess(uploadUrl) - - if (isAccessible) { - console.log('鉁� 鏂囦欢鍙甯歌闂�') - } else { - console.log('鈿狅笍 鏂囦欢璁块棶楠岃瘉澶辫触锛屼絾涓婁紶鍙兘鎴愬姛') - } - - console.log('=== Logo涓婁紶娴嬭瘯瀹屾垚 ===') - - return { - success: true, - url: uploadUrl, - config: config - } - - } catch (error: any) { - console.error('鉂� Logo涓婁紶娴嬭瘯澶辫触:', error.message) - console.error('閿欒璇︽儏:', error) - - return { - success: false, - error: error.message - } - } -} - -/** - * 鑾峰彇Logo鏂囦欢 - */ -async function fetchLogoFile(): Promise<File | null> { - try { - console.log('灏濊瘯浠� /UI/logo.jpg 鑾峰彇鏂囦欢...') - const response = await fetch('/UI/logo.jpg') - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - - const blob = await response.blob() - const file = new File([blob], 'logo.jpg', { type: 'image/jpeg' }) - - console.log('鎴愬姛鑾峰彇Logo鏂囦欢:', { - size: file.size, - type: file.type - }) - - return file - - } catch (error) { - console.error('鑾峰彇Logo鏂囦欢澶辫触:', error) - return null - } -} - -/** - * 楠岃瘉鏂囦欢鏄惁鍙闂� - */ -async function verifyFileAccess(url: string): Promise<boolean> { - try { - const response = await fetch(url, { method: 'HEAD' }) - return response.ok - } catch (error) { - console.error('楠岃瘉鏂囦欢璁块棶澶辫触:', error) - return false - } -} - -/** - * 鍦ㄦ祻瑙堝櫒鎺у埗鍙颁腑杩愯娴嬭瘯 - */ -export const runLogoUploadTest = () => { - console.log('寮�濮嬫墽琛孡ogo涓婁紶娴嬭瘯...') - - testUploadLogo() - .then(result => { - if (result.success) { - console.log('馃帀 娴嬭瘯鎴愬姛瀹屾垚!') - console.log('Logo URL:', result.url) - - // 鍙互灏哢RL淇濆瓨鍒發ocalStorage渚涘悗缁娇鐢� - localStorage.setItem('uploaded_logo_url', result.url!) - console.log('Logo URL宸蹭繚瀛樺埌localStorage') - - } else { - console.log('鉂� 娴嬭瘯澶辫触:', result.error) - } - }) - .catch(error => { - console.error('鉂� 娴嬭瘯鎵ц寮傚父:', error) - }) -} - -// 瀵煎嚭鍒板叏灞�瀵硅薄锛屾柟渚垮湪娴忚鍣ㄦ帶鍒跺彴涓皟鐢� -if (typeof window !== 'undefined') { - (window as any).runLogoUploadTest = runLogoUploadTest - (window as any).testUploadLogo = testUploadLogo -} \ No newline at end of file diff --git a/web/src/utils/cos-simple.ts b/web/src/utils/cos-simple.ts index 9190707..24a983d 100644 --- a/web/src/utils/cos-simple.ts +++ b/web/src/utils/cos-simple.ts @@ -38,16 +38,11 @@ */ export const uploadToCOS = async (file: File): Promise<string> => { try { - console.log('寮�濮嬭幏鍙栦笂浼犲嚟璇�...') - // 鑾峰彇涓婁紶鍑瘉 const credentials = await getUploadCredentials() - console.log('鑾峰彇鍒颁笂浼犲嚟璇�:', credentials) // 浣跨敤棰勭鍚峌RL const uploadUrl = credentials.presignedUrl - - console.log('寮�濮嬩笂浼犳枃浠跺埌:', uploadUrl) // 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢 const uploadResponse = await axios.put(uploadUrl, file, { @@ -57,7 +52,6 @@ }) if (uploadResponse.status === 200) { - console.log('鏂囦欢涓婁紶鎴愬姛!') // 杩斿洖鏂囦欢鐨勮闂甎RL锛堝幓鎺夋煡璇㈠弬鏁帮級 const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}` return fileUrl @@ -66,7 +60,6 @@ } } catch (error) { - console.error('涓婁紶鏂囦欢澶辫触:', error) throw error } } \ No newline at end of file diff --git a/web/src/utils/cos.ts b/web/src/utils/cos.ts index d8889fc..b37e3b2 100644 --- a/web/src/utils/cos.ts +++ b/web/src/utils/cos.ts @@ -7,7 +7,6 @@ const response = await axios.get('http://localhost:8080/api/cos/credentials') return response.data } catch (error) { - console.error('浠庡悗绔幏鍙栦复鏃跺瘑閽ュけ璐�:', error) throw error } } diff --git a/web/src/utils/upload-logo-browser.ts b/web/src/utils/upload-logo-browser.ts index b8c6eb2..c685217 100644 --- a/web/src/utils/upload-logo-browser.ts +++ b/web/src/utils/upload-logo-browser.ts @@ -21,12 +21,8 @@ } } - console.log('寮�濮嬩笂浼爈ogo鏂囦欢:', logoFile.name, logoFile.size, 'bytes') - // 涓婁紶鍒癈OS鐨刟vatars鐩綍 const url = await uploadToCOS(logoFile, 'avatars/') - - console.log('Logo涓婁紶鎴愬姛:', url) return { success: true, @@ -56,7 +52,7 @@ return new File([blob], 'logo.jpg', { type: 'image/jpeg' }) } } catch (error) { - console.log('浠巔ublic鐩綍鑾峰彇logo澶辫触锛屽皾璇曞叾浠栨柟寮�') + // 浠巔ublic鐩綍鑾峰彇logo澶辫触锛屽皾璇曞叾浠栨柟寮� } try { @@ -67,7 +63,7 @@ return new File([blob], 'logo.jpg', { type: 'image/jpeg' }) } } catch (error) { - console.log('浠嶶I鐩綍鑾峰彇logo澶辫触') + // 浠嶶I鐩綍鑾峰彇logo澶辫触 } // 鏂瑰紡3: 杩斿洖null锛岄渶瑕佺敤鎴锋墜鍔ㄩ�夋嫨鏂囦欢 diff --git a/web/src/views/ActivityForm.vue b/web/src/views/ActivityForm.vue index 3425b07..4e6d7d3 100644 --- a/web/src/views/ActivityForm.vue +++ b/web/src/views/ActivityForm.vue @@ -79,7 +79,10 @@ </el-form-item> <!-- 鍥剧墖/瑙嗛涓婁紶 --> - <el-divider content-position="left">鍥剧墖/瑙嗛</el-divider> + <el-divider content-position="left"> + 鍥剧墖/瑙嗛 + <span class="media-description">鏀寔jpg/png/mp4锛屾渶澶�3涓枃浠�</span> + </el-divider> <el-form-item label="濯掍綋鏂囦欢"> <div class="media-upload-section"> @@ -109,7 +112,7 @@ <!-- 娣诲姞鎸夐挳 --> <el-upload v-if="form.mediaFiles.length < 3" - class="media-uploader" + class="media-uploader media-uploader-left" :show-file-list="false" :before-upload="beforeMediaUpload" action="#" @@ -120,7 +123,6 @@ <div class="upload-placeholder"> <el-icon class="upload-icon"><Plus /></el-icon> <div class="upload-text">娣诲姞鍥剧墖/瑙嗛</div> - <div class="upload-tip">鏀寔jpg/png/mp4锛屾渶澶�3涓枃浠�</div> </div> </el-upload> </div> @@ -141,7 +143,7 @@ </div> </div> - <div v-if="form.stages && form.stages.length > 0" class="stages-list"> + <div v-if="form.value && form.value.stages && form.value.stages.length > 0" class="stages-list"> <div v-for="(stage, index) in sortedFormStages" :key="index" class="stage-item"> <div class="stage-info"> <div class="stage-header"> @@ -191,9 +193,8 @@ </el-tag> </template> </el-table-column> - <el-table-column label="鎿嶄綔" width="180" align="center"> + <el-table-column label="鎿嶄綔" width="100" align="center"> <template #default="{ row, $index }"> - <el-button size="small" @click="editJudge(row, $index)">缂栬緫</el-button> <el-button size="small" type="danger" @click="removeJudge($index)">鍒犻櫎</el-button> </template> </el-table-column> @@ -321,14 +322,16 @@ <!-- 闃舵閫夋嫨 --> <div style="margin-bottom: 16px;"> - <el-form-item label="娣诲姞鍒伴樁娈碉細" label-width="100px"> - <el-select v-model="selectedStageOption" style="width: 100%;" @change="handleStageChange"> - <!-- 鍙樉绀烘瘮璧涢樁娈� --> + <el-form-item label="璐熻矗闃舵锛�" label-width="100px"> + <!-- 璋冭瘯淇℃伅 --> + + <el-select v-model="selectedStageOptions" multiple style="width: 100%;" placeholder="璇烽�夋嫨璐熻矗鐨勯樁娈�"> + <!-- 浣跨敤璁$畻灞炴�� --> <el-option - v-for="stage in form.stages" - :key="stage.id" - :label="stage.name" - :value="stage.id ? stage.id.toString() : ''" + v-for="option in stageOptions" + :key="option.value" + :label="option.label" + :value="option.value" /> </el-select> </el-form-item> @@ -393,7 +396,7 @@ <el-select v-model="currentStudent.lastStageId" placeholder="璇烽�夋嫨闃舵" style="width: 100%"> <el-option label="鏃�" :value="null" /> <el-option - v-for="stage in form.stages" + v-for="stage in (form.value?.stages || [])" :key="stage.id" :label="stage.name" :value="stage.id" @@ -413,7 +416,7 @@ </template> <script setup> -import { ref, onMounted, computed } from 'vue' +import { ref, onMounted, computed, nextTick } from 'vue' import { useRouter, useRoute } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' import { Plus, VideoPlay, Clock, User, UserFilled, Search } from '@element-plus/icons-vue' @@ -466,7 +469,7 @@ // 璇勫閫夋嫨鐩稿叧 const allJudges = ref([]) const judgeSearchText = ref('') -const selectedStageOption = ref('all') +const selectedStageOptions = ref([]) const selectedJudges = ref([]) const judgeLoading = ref(false) @@ -521,6 +524,21 @@ }) }) +// 鐢ㄤ簬涓嬫媺妗嗙殑闃舵閫夐」 +const stageOptions = computed(() => { + if (!form.value?.stages) { + return [] + } + + return form.value.stages + .filter(stage => stage && stage.id != null) + .map(stage => ({ + label: stage.name, + value: stage.id.toString(), + stage: stage + })) +}) + // 琛ㄥ崟楠岃瘉瑙勫垯 const rules = { name: [ @@ -563,7 +581,6 @@ judgeLoading.value = true const judges = await getAllJudges() allJudges.value = judges || [] - console.log('鍔犺浇璇勫鍒楄〃鎴愬姛:', allJudges.value.length, '涓瘎濮�') } catch (error) { console.error('鍔犺浇璇勫鍒楄〃澶辫触:', error) ElMessage.error('鍔犺浇璇勫鍒楄〃澶辫触: ' + error.message) @@ -580,6 +597,7 @@ try { loading.value = true const activity = await getActivity(route.params.id) + if (activity) { form.value = { id: activity.id, @@ -599,12 +617,8 @@ // 鍔犺浇骞跺洖濉凡涓婁紶濯掍綋锛歵argetType=2 鍋囪涓衡�滄椿鍔ㄢ�濓紝濡備笉鍚岃璋冩暣 try { const medias = await getMediasByTarget(MediaTargetType.ACTIVITY, parseInt(activity.id)) - console.log('=== 鍔犺浇娲诲姩濯掍綋璋冭瘯淇℃伅 ===') - console.log('娲诲姩ID:', activity.id) - console.log('鑾峰彇鍒扮殑濯掍綋鏁版嵁:', medias) form.value.mediaFiles = (medias || []).map(m => { - console.log('澶勭悊濯掍綋鏂囦欢:', m) const isImage = (m.mediaType === 1) || (m.fileExt && ['jpg','jpeg','png','gif','webp'].includes(m.fileExt.toLowerCase())) const isVideo = (m.mediaType === 2) || (m.fileExt && ['mp4','mov','m4v','avi','mkv'].includes(m.fileExt.toLowerCase())) const mediaItem = { @@ -615,16 +629,14 @@ uploaded: true, // 鏍囪涓哄凡涓婁紶锛屼笉闇�瑕侀噸鏂颁笂浼� file: null // 宸蹭繚瀛樼殑鏂囦欢娌℃湁file瀵硅薄 } - console.log('杞崲鍚庣殑濯掍綋椤�:', mediaItem) return mediaItem }) - console.log('鏈�缁堢殑mediaFiles:', form.value.mediaFiles) - - // 璁剧疆闃舵鏁伴噺閫夋嫨鍣ㄧ殑鍊� - selectedStageCount.value = form.value.stages.length || 1 } catch (e) { console.error('鍔犺浇娲诲姩濯掍綋澶辫触:', e) } + + // 璁剧疆闃舵鏁伴噺閫夋嫨鍣ㄧ殑鍊� + selectedStageCount.value = (form.value && form.value.stages) ? form.value.stages.length || 1 : 1 } } catch (error) { console.error('鍔犺浇姣旇禌鏁版嵁澶辫触:', error) @@ -637,7 +649,7 @@ // 闃舵绠$悊 // 闃舵鏁伴噺鍙樺寲澶勭悊 const onStageCountChange = (count) => { - if (!count) return + if (!count || !form.value || !form.value.stages) return // 濡傛灉褰撳墠闃舵鏁伴噺灏戜簬閫夋嫨鐨勬暟閲忥紝鑷姩娣诲姞闃舵 while (form.value.stages.length < count) { @@ -672,6 +684,7 @@ // 鑾峰彇闃舵鍦ㄥ師濮嬫暟缁勪腑鐨勭储寮� const getOriginalStageIndex = (stage) => { + if (!form.value || !form.value.stages) return -1 return form.value.stages.findIndex(s => s === stage) } @@ -688,6 +701,8 @@ } const removeStage = async (index) => { + if (!form.value || !form.value.stages) return + try { await ElMessageBox.confirm('纭畾瑕佸垹闄よ繖涓樁娈靛悧锛�', '鎻愮ず', { confirmButtonText: '纭畾', @@ -725,6 +740,8 @@ } const saveStage = async () => { + if (!form.value || !form.value.stages) return + try { await stageFormRef.value.validate() @@ -787,6 +804,11 @@ } const removeJudge = async (index) => { + if (!form.value || !form.value.judges) { + ElMessage.error('琛ㄥ崟鏁版嵁鏈垵濮嬪寲') + return + } + try { await ElMessageBox.confirm('纭畾瑕佸垹闄よ繖涓瘎濮斿悧锛�', '鎻愮ず', { confirmButtonText: '纭畾', @@ -801,43 +823,40 @@ } const getJudgeStages = (judge) => { - if (!judge.stageIds) return [] + if (!judge.stageIds || !form.value || !form.value.stages) return [] const stages = [] - // 鍙鏌ユ瘮璧涢樁娈� - if (form.value.stages) { - const matchedStages = form.value.stages.filter(stage => judge.stageIds.includes(stage.id)) - matchedStages.forEach(stage => { + // 妫�鏌ユ瘮璧涢樁娈� + judge.stageIds.forEach(stageId => { + // 澶勭悊瀹為檯闃舵ID + const stage = form.value.stages.find(s => s.id === stageId) + if (stage) { stages.push({ id: stage.id, name: stage.name }) - }) - } + } + }) return stages } const resetJudgeDialog = () => { judgeSearchText.value = '' - // 榛樿閫夋嫨绗竴涓樁娈� - selectedStageOption.value = form.value.stages && form.value.stages.length > 0 - ? form.value.stages[0].id?.toString() || '' - : '' + // 娓呯┖闃舵閫夋嫨 + selectedStageOptions.value = [] selectedJudges.value = [] } const handleJudgeSearch = (value) => { - console.log('鎼滅储璇勫:', value) + // 鎼滅储璇勫 } -const handleStageChange = (value) => { - console.log('閫夋嫨闃舵:', value) -} + const handleJudgeSelectionChange = (value) => { - console.log('閫夋嫨璇勫:', value) + // 閫夋嫨璇勫 } const toggleSelectAll = () => { @@ -859,6 +878,17 @@ return } + if (!form.value || !form.value.judges) { + ElMessage.error('琛ㄥ崟鏁版嵁鏈垵濮嬪寲') + return + } + + // 濡傛灉鏈夐樁娈典絾娌℃湁閫夋嫨闃舵锛屽垯鎻愮ず + if (form.value && form.value.stages && form.value.stages.length > 0 && selectedStageOptions.value.length === 0) { + ElMessage.warning('璇烽�夋嫨鑷冲皯涓�涓礋璐i樁娈�') + return + } + let addedCount = 0 selectedJudges.value.forEach(judgeId => { @@ -867,14 +897,20 @@ // 妫�鏌ユ槸鍚﹀凡缁忓瓨鍦� const existingJudge = form.value.judges.find(j => j.id === judgeId) if (existingJudge) { - // 鏇存柊鐜版湁璇勫鐨勯樁娈� - const stageId = parseInt(selectedStageOption.value) - if (!existingJudge.stageIds.includes(stageId)) { - existingJudge.stageIds.push(stageId) + // 鏇存柊鐜版湁璇勫鐨勯樁娈碉紝鍚堝苟鏂伴�夋嫨鐨勯樁娈� + if (selectedStageOptions.value.length > 0) { + selectedStageOptions.value.forEach(stageId => { + const stageIdInt = parseInt(stageId) + if (!existingJudge.stageIds.includes(stageIdInt)) { + existingJudge.stageIds.push(stageIdInt) + } + }) } } else { - // 娣诲姞鏂拌瘎濮� - const stageIds = [parseInt(selectedStageOption.value)] + // 娣诲姞鏂拌瘎濮旓紝鍖呭惈鎵�鏈夐�夋嫨鐨勯樁娈� + const stageIds = selectedStageOptions.value.length > 0 + ? selectedStageOptions.value.map(id => parseInt(id)) + : [] const newJudge = { id: judge.id, @@ -886,6 +922,10 @@ } } }) + + // 娓呯┖閫夋嫨 + selectedJudges.value = [] + selectedStageOptions.value = [] judgeDialogVisible.value = false ElMessage.success(`鎴愬姛娣诲姞 ${addedCount} 涓瘎濮擿) @@ -940,7 +980,7 @@ } const getLastStage = (student) => { - if (!student.lastStageId || !form.value.stages) return '鏃�' + if (!student.lastStageId || !form.value || !form.value.stages) return '鏃�' const stage = form.value.stages.find(s => s.id === student.lastStageId) return stage ? stage.name : '鏃�' } @@ -985,13 +1025,17 @@ uploaded: false // 鏍囪涓烘湭涓婁紶 } - form.value.mediaFiles.push(mediaFile) - ElMessage.success('鏂囦欢宸查�夋嫨锛岀偣鍑绘洿鏂版寜閽椂灏嗕笂浼�') + if (form.value && form.value.mediaFiles) { + form.value.mediaFiles.push(mediaFile) + ElMessage.success('鏂囦欢宸查�夋嫨锛岀偣鍑绘洿鏂版寜閽椂灏嗕笂浼�') + } return false // 闃绘el-upload鐨勯粯璁や笂浼� } const beforeMediaUpload = (file) => { + if (!form.value || !form.value.mediaFiles) return false + if (form.value.mediaFiles.length >= 3) { ElMessage.error('鏈�澶氬彧鑳戒笂浼�3涓枃浠�!') return false @@ -1056,6 +1100,8 @@ // 澶勭悊濯掍綋鏂囦欢涓婁紶 const handleMediaUpload = async (activityId) => { + if (!form.value || !form.value.mediaFiles) return + try { for (const mediaFile of form.value.mediaFiles) { // 璺宠繃宸茬粡鏈� id 鐨勫獟浣撴枃浠讹紙宸蹭繚瀛樼殑锛� @@ -1074,10 +1120,8 @@ } try { - console.log('寮�濮嬩笂浼犳枃浠�:', mediaFile.name) // 1. 涓婁紶鏂囦欢鍒版湇鍔″櫒 const uploadResult = await uploadFile(mediaFile.file) - console.log('鏂囦欢涓婁紶鎴愬姛:', uploadResult) // 2. 淇濆瓨濯掍綋淇℃伅鍒版暟鎹簱 const mediaInput = { @@ -1089,11 +1133,7 @@ targetType: MediaTargetType.ACTIVITY, // 娲诲姩 targetId: parseInt(activityId) // 杞崲涓烘暟瀛楃被鍨� } - - console.log('鍑嗗淇濆瓨濯掍綋淇℃伅:', mediaInput) - console.log('娲诲姩ID:', activityId) const savedMedia = await saveMedia(mediaInput) - console.log(`濯掍綋鏂囦欢 ${mediaFile.name} 涓婁紶骞朵繚瀛樻垚鍔�:`, savedMedia) // 鏇存柊濯掍綋鏂囦欢淇℃伅 mediaFile.id = savedMedia.id @@ -1117,6 +1157,8 @@ // 鎻愪氦琛ㄥ崟 const handleSubmit = async () => { if (submitting.value) return + if (!form.value) return + try { await formRef.value.validate() @@ -1124,8 +1166,6 @@ // 鍑嗗淇濆瓨鏁版嵁锛屽彧鍖呭惈鍚庣鏀寔鐨勫瓧娈� const saveData = { - id: form.value.id, - pid: form.value.pid || 0, name: form.value.name, description: form.value.description, signupDeadline: form.value.signupDeadline, @@ -1134,22 +1174,41 @@ ratingSchemeId: form.value.ratingSchemeId, playerMax: form.value.playerMax, state: form.value.state || 1, - stages: form.value.stages ? form.value.stages.map(stage => ({ - id: stage.id, - name: stage.name, - description: stage.description, - matchTime: stage.matchTime, - address: stage.address, - ratingSchemeId: stage.ratingSchemeId, - playerMax: stage.playerMax, - sortOrder: stage.sortOrder, - state: stage.state || 1 - })) : [], - judges: form.value.judges ? form.value.judges.map(judge => ({ + stages: form.value.stages ? form.value.stages.map(stage => { + const stageData = { + name: stage.name, + description: stage.description, + matchTime: stage.matchTime, + address: stage.address, + playerMax: stage.playerMax, + sortOrder: stage.sortOrder, + state: stage.state || 1 + } + // 鍙湪鏈夋湁鏁圛D鏃舵墠娣诲姞id瀛楁 + if (stage.id) { + stageData.id = stage.id + } + // 鍙湪鏈夋湁鏁坮atingSchemeId鏃舵墠娣诲姞璇ュ瓧娈� + if (stage.ratingSchemeId) { + stageData.ratingSchemeId = stage.ratingSchemeId + } + return stageData + }) : [], + judges: form.value.judges ? form.value.judges.filter(judge => judge.id && judge.name).map(judge => ({ judgeId: judge.id, judgeName: judge.name, stageIds: judge.stageIds || [] })) : [] + } + + // 濡傛灉鏄紪杈戞ā寮忥紝娣诲姞id瀛楁 + if (isEdit.value && form.value.id) { + saveData.id = form.value.id + } + + // 濡傛灉鏈塸id锛屾坊鍔爌id瀛楁 + if (form.value.pid) { + saveData.pid = form.value.pid } const result = await saveActivity(saveData) @@ -1195,7 +1254,7 @@ await loadActivity() // 濡傛灉鏄柊寤烘ā寮忎笖娌℃湁闃舵锛岃嚜鍔ㄥ垱寤轰竴涓樁娈� - if (!isEdit.value && form.value.stages.length === 0) { + if (!isEdit.value && form.value && form.value.stages && form.value.stages.length === 0) { onStageCountChange(1) } }) @@ -1572,4 +1631,24 @@ flex: 1; padding-left: 8px; } + +/* 濯掍綋鎻忚堪鏂囨湰鏍峰紡 */ +.media-description { + font-size: 12px; + color: #909399; + font-weight: normal; + margin-left: 8px; +} + +/* 濯掍綋涓婁紶鎸夐挳 */ +.media-uploader-left { + margin-left: 16px; +} + +.media-container { + display: flex; + align-items: flex-start; + gap: 16px; + flex-wrap: wrap; +} </style> \ No newline at end of file diff --git a/web/src/views/RatingSchemeForm.vue b/web/src/views/RatingSchemeForm.vue deleted file mode 100644 index 3b49b69..0000000 --- a/web/src/views/RatingSchemeForm.vue +++ /dev/null @@ -1,432 +0,0 @@ -<template> - <div class="rating-scheme-form"> - <!-- 椤甸潰鏍囬 --> - <div class="page-header"> - <h2>{{ isEdit ? '缂栬緫璇勫垎妯℃澘' : '鏂板璇勫垎妯℃澘' }}</h2> - <el-button @click="handleBack">杩斿洖鍒楄〃</el-button> - </div> - - <!-- 琛ㄥ崟鍐呭 --> - <div class="form-container"> - <el-form - ref="formRef" - :model="form" - :rules="rules" - label-width="120px" - v-loading="loading" - > - <!-- 鍩烘湰淇℃伅 --> - <el-card class="form-card" header="鍩烘湰淇℃伅"> - <el-form-item label="妯℃澘鍚嶇О" prop="name"> - <el-input - v-model="form.name" - placeholder="璇疯緭鍏ユā鏉垮悕绉�" - maxlength="30" - show-word-limit - /> - </el-form-item> - - <el-form-item label="妯℃澘鎻忚堪" prop="description"> - <el-input - v-model="form.description" - type="textarea" - :rows="3" - placeholder="璇疯緭鍏ユā鏉挎弿杩�" - maxlength="500" - show-word-limit - /> - </el-form-item> - </el-card> - - <!-- 璇勫垎鏉$洰 --> - <el-card class="form-card" header="璇勫垎鏉$洰"> - <div class="items-header"> - <span>璇勫垎鏉$洰閰嶇疆</span> - <el-button type="primary" size="small" @click="handleAddItem"> - <el-icon><Plus /></el-icon> - 娣诲姞鏉$洰 - </el-button> - </div> - - <div class="items-list" v-if="form.items && form.items.length > 0"> - <div - v-for="(item, index) in form.items" - :key="index" - class="item-row" - > - <el-row :gutter="20" align="middle"> - <el-col :span="1"> - <span class="item-index">{{ index + 1 }}</span> - </el-col> - <el-col :span="8"> - <el-form-item - :prop="`items.${index}.name`" - :rules="itemRules.name" - label-width="0" - > - <el-input - v-model="item.name" - placeholder="鏉$洰鍚嶇О" - maxlength="30" - /> - </el-form-item> - </el-col> - <el-col :span="6"> - <el-form-item - :prop="`items.${index}.maxScore`" - :rules="itemRules.maxScore" - label-width="0" - > - <el-input-number - v-model="item.maxScore" - :min="1" - :max="100" - placeholder="鏈�澶у垎鍊�" - style="width: 100%" - /> - </el-form-item> - </el-col> - <el-col :span="6"> - <div class="item-actions"> - <el-button - type="text" - size="small" - @click="handleMoveUp(index)" - :disabled="index === 0" - > - <el-icon><ArrowUp /></el-icon> - </el-button> - <el-button - type="text" - size="small" - @click="handleMoveDown(index)" - :disabled="index === form.items.length - 1" - > - <el-icon><ArrowDown /></el-icon> - </el-button> - <el-button - type="text" - size="small" - @click="handleRemoveItem(index)" - style="color: #f56c6c" - > - <el-icon><Delete /></el-icon> - </el-button> - </div> - </el-col> - <el-col :span="3"> - <div class="item-score"> - <el-tag type="success">{{ item.maxScore || 0 }}鍒�</el-tag> - </div> - </el-col> - </el-row> - </div> - </div> - - <div v-else class="empty-items"> - <el-empty description="鏆傛棤璇勫垎鏉$洰锛岃娣诲姞鏉$洰" /> - </div> - - <!-- 鎬诲垎鏄剧ず --> - <div class="total-score"> - <span>妯℃澘鎬诲垎锛�</span> - <el-tag type="primary" size="large">{{ totalScore }}鍒�</el-tag> - </div> - </el-card> - - <!-- 鎿嶄綔鎸夐挳 --> - <div class="form-actions"> - <el-button type="primary" @click="handleSubmit" :loading="submitting"> - {{ isEdit ? '鏇存柊' : '淇濆瓨' }} - </el-button> - <el-button @click="handleBack">鍙栨秷</el-button> - </div> - </el-form> - </div> - </div> -</template> - -<script> -import { ref, reactive, computed, onMounted } from 'vue' -import { useRouter, useRoute } from 'vue-router' -import { ElMessage } from 'element-plus' -import { Plus, ArrowUp, ArrowDown, Delete } from '@element-plus/icons-vue' -import { getRatingScheme, saveRatingScheme } from '@/api/rating' - -export default { - name: 'RatingSchemeForm', - components: { - Plus, - ArrowUp, - ArrowDown, - Delete - }, - setup() { - const router = useRouter() - const route = useRoute() - - // 鍝嶅簲寮忔暟鎹� - const formRef = ref() - const loading = ref(false) - const submitting = ref(false) - - const form = reactive({ - id: null, - name: '', - description: '', - items: [] - }) - - // 琛ㄥ崟楠岃瘉瑙勫垯 - const rules = { - name: [ - { required: true, message: '璇疯緭鍏ユā鏉垮悕绉�', trigger: 'blur' }, - { min: 1, max: 30, message: '妯℃澘鍚嶇О闀垮害鍦�1鍒�30涓瓧绗�', trigger: 'blur' } - ] - } - - const itemRules = { - name: [ - { required: true, message: '璇疯緭鍏ユ潯鐩悕绉�', trigger: 'blur' }, - { min: 1, max: 30, message: '鏉$洰鍚嶇О闀垮害鍦�1鍒�30涓瓧绗�', trigger: 'blur' } - ], - maxScore: [ - { required: true, message: '璇疯緭鍏ユ渶澶у垎鍊�', trigger: 'blur' }, - { type: 'number', min: 1, max: 100, message: '鍒嗗�艰寖鍥村湪1鍒�100涔嬮棿', trigger: 'blur' } - ] - } - - // 璁$畻灞炴�� - const isEdit = computed(() => !!route.params.id) - - const totalScore = computed(() => { - return form.items.reduce((sum, item) => sum + (item.maxScore || 0), 0) - }) - - // 鍔犺浇鏁版嵁 - const loadData = async () => { - if (!isEdit.value) return - - try { - loading.value = true - const data = await getRatingScheme(route.params.id) - - form.id = data.id - form.name = data.name - form.description = data.description - form.items = data.items || [] - } catch (error) { - console.error('鍔犺浇璇勫垎妯℃澘澶辫触:', error) - ElMessage.error('鍔犺浇鏁版嵁澶辫触') - handleBack() - } finally { - loading.value = false - } - } - - // 娣诲姞鏉$洰 - const handleAddItem = () => { - form.items.push({ - id: null, - name: '', - maxScore: 10, - orderNo: form.items.length + 1 - }) - } - - // 鍒犻櫎鏉$洰 - const handleRemoveItem = (index) => { - form.items.splice(index, 1) - // 閲嶆柊璁剧疆鎺掑簭鍙� - form.items.forEach((item, idx) => { - item.orderNo = idx + 1 - }) - } - - // 涓婄Щ鏉$洰 - const handleMoveUp = (index) => { - if (index > 0) { - const temp = form.items[index] - form.items[index] = form.items[index - 1] - form.items[index - 1] = temp - - // 閲嶆柊璁剧疆鎺掑簭鍙� - form.items.forEach((item, idx) => { - item.orderNo = idx + 1 - }) - } - } - - // 涓嬬Щ鏉$洰 - const handleMoveDown = (index) => { - if (index < form.items.length - 1) { - const temp = form.items[index] - form.items[index] = form.items[index + 1] - form.items[index + 1] = temp - - // 閲嶆柊璁剧疆鎺掑簭鍙� - form.items.forEach((item, idx) => { - item.orderNo = idx + 1 - }) - } - } - - // 鎻愪氦琛ㄥ崟 - const handleSubmit = async () => { - try { - // 楠岃瘉琛ㄥ崟 - await formRef.value.validate() - - // 楠岃瘉鑷冲皯鏈変竴涓潯鐩� - if (!form.items || form.items.length === 0) { - ElMessage.error('璇疯嚦灏戞坊鍔犱竴涓瘎鍒嗘潯鐩�') - return - } - - submitting.value = true - - // 鍑嗗鎻愪氦鏁版嵁 - const submitData = { - id: form.id, - name: form.name, - description: form.description, - items: form.items.map((item, index) => ({ - id: item.id, - name: item.name, - maxScore: item.maxScore, - orderNo: index + 1 - })) - } - - await saveRatingScheme(submitData) - - ElMessage.success(isEdit.value ? '鏇存柊鎴愬姛' : '淇濆瓨鎴愬姛') - handleBack() - } catch (error) { - console.error('淇濆瓨璇勫垎妯℃澘澶辫触:', error) - ElMessage.error(error.message || '淇濆瓨澶辫触') - } finally { - submitting.value = false - } - } - - // 杩斿洖鍒楄〃 - const handleBack = () => { - router.push('/rating-scheme') - } - - // 鍒濆鍖� - onMounted(() => { - loadData() - }) - - return { - formRef, - loading, - submitting, - form, - rules, - itemRules, - isEdit, - totalScore, - handleAddItem, - handleRemoveItem, - handleMoveUp, - handleMoveDown, - handleSubmit, - handleBack - } - } -} -</script> - -<style scoped> -.rating-scheme-form { - padding: 20px; -} - -.page-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; -} - -.page-header h2 { - margin: 0; - color: #303133; -} - -.form-container { - max-width: 1000px; -} - -.form-card { - margin-bottom: 20px; -} - -.items-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - font-weight: bold; -} - -.items-list { - margin-bottom: 20px; -} - -.item-row { - margin-bottom: 15px; - padding: 15px; - border: 1px solid #ebeef5; - border-radius: 4px; - background: #fafafa; -} - -.item-index { - display: inline-flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - background: #409eff; - color: white; - border-radius: 50%; - font-size: 12px; - font-weight: bold; -} - -.item-actions { - display: flex; - gap: 5px; - justify-content: center; -} - -.item-score { - text-align: center; -} - -.empty-items { - margin: 40px 0; -} - -.total-score { - text-align: right; - padding: 15px 0; - border-top: 1px solid #ebeef5; - font-size: 16px; - font-weight: bold; -} - -.form-actions { - text-align: center; - padding: 20px 0; -} - -.form-actions .el-button { - margin: 0 10px; - min-width: 100px; -} -</style> \ No newline at end of file diff --git a/web/src/views/RatingSchemeList.vue b/web/src/views/RatingSchemeList.vue deleted file mode 100644 index 3a631d9..0000000 --- a/web/src/views/RatingSchemeList.vue +++ /dev/null @@ -1,254 +0,0 @@ -<template> - <div class="rating-scheme-list"> - <!-- 椤甸潰鏍囬 --> - <div class="page-header"> - <h2>璇勫垎妯℃澘绠$悊</h2> - </div> - - <!-- 鎼滅储鍜屾搷浣滃尯鍩� --> - <div class="search-bar"> - <div class="search-form"> - <el-input - v-model="searchForm.name" - placeholder="璇疯緭鍏ユā鏉垮悕绉�" - clearable - @keyup.enter="handleSearch" - style="width: 200px" - > - <template #prefix> - <el-icon><Search /></el-icon> - </template> - </el-input> - <el-button type="primary" @click="handleSearch"> - <el-icon><Search /></el-icon> - 鏌ヨ - </el-button> - <el-button @click="handleReset">閲嶇疆</el-button> - <el-button type="primary" @click="handleAdd"> - <el-icon><Plus /></el-icon> - 鏂板妯℃澘 - </el-button> - </div> - </div> - - <!-- 鏁版嵁琛ㄦ牸 --> - <div class="table-container"> - <el-table - :data="tableData" - v-loading="loading" - stripe - style="width: 100%" - > - <el-table-column prop="name" label="妯℃澘鍚嶇О" min-width="200" /> - <el-table-column prop="totalScore" label="妯℃澘鎬诲垎" width="120" align="center"> - <template #default="{ row }"> - <el-tag type="success">{{ row.totalScore }}鍒�</el-tag> - </template> - </el-table-column> - <el-table-column prop="description" label="妯℃澘鎻忚堪" min-width="300" show-overflow-tooltip /> - <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" /> - <el-table-column label="鎿嶄綔" width="200" fixed="right"> - <template #default="{ row }"> - <el-button type="primary" size="small" @click="handleEdit(row)"> - 缂栬緫 - </el-button> - <el-button type="danger" size="small" @click="handleDelete(row)"> - 鍒犻櫎 - </el-button> - </template> - </el-table-column> - </el-table> - </div> - - <!-- 鍒嗛〉 --> - <div class="pagination-container"> - <el-pagination - v-model:current-page="pagination.page" - v-model:page-size="pagination.size" - :page-sizes="[10, 20, 50, 100]" - :total="pagination.total" - layout="total, sizes, prev, pager, next, jumper" - @size-change="handleSizeChange" - @current-change="handleCurrentChange" - /> - </div> - </div> -</template> - -<script> -import { ref, reactive, onMounted } from 'vue' -import { useRouter } from 'vue-router' -import { ElMessage, ElMessageBox } from 'element-plus' -import { Search, Plus } from '@element-plus/icons-vue' -import { getRatingSchemes, deleteRatingScheme } from '@/api/rating' - -export default { - name: 'RatingSchemeList', - components: { - Search, - Plus - }, - setup() { - const router = useRouter() - - // 鍝嶅簲寮忔暟鎹� - const loading = ref(false) - const tableData = ref([]) - - const searchForm = reactive({ - name: '' - }) - - const pagination = reactive({ - page: 1, - size: 10, - total: 0 - }) - - // 鍔犺浇鏁版嵁 - const loadData = async () => { - try { - loading.value = true - const params = { - page: pagination.page - 1, // 鍚庣浠�0寮�濮� - size: pagination.size, - name: searchForm.name || undefined - } - - const result = await getRatingSchemes(params.page, params.size, params.name) - tableData.value = result.content - pagination.total = result.totalElements - } catch (error) { - console.error('鍔犺浇璇勫垎妯℃澘鍒楄〃澶辫触:', error) - ElMessage.error('鍔犺浇鏁版嵁澶辫触') - } finally { - loading.value = false - } - } - - // 鎼滅储 - const handleSearch = () => { - pagination.page = 1 - loadData() - } - - // 閲嶇疆 - const handleReset = () => { - searchForm.name = '' - pagination.page = 1 - loadData() - } - - // 鏂板 - const handleAdd = () => { - router.push('/rating-scheme/form') - } - - // 缂栬緫 - const handleEdit = (row) => { - router.push(`/rating-scheme/form/${row.id}`) - } - - // 鍒犻櫎 - const handleDelete = async (row) => { - try { - await ElMessageBox.confirm( - `纭畾瑕佸垹闄よ瘎鍒嗘ā鏉�"${row.name}"鍚楋紵`, - '纭鍒犻櫎', - { - confirmButtonText: '纭畾', - cancelButtonText: '鍙栨秷', - type: 'warning' - } - ) - - await deleteRatingScheme(row.id) - ElMessage.success('鍒犻櫎鎴愬姛') - loadData() - } catch (error) { - if (error !== 'cancel') { - console.error('鍒犻櫎璇勫垎妯℃澘澶辫触:', error) - ElMessage.error(error.message || '鍒犻櫎澶辫触') - } - } - } - - // 鍒嗛〉浜嬩欢 - const handleSizeChange = (size) => { - pagination.size = size - pagination.page = 1 - loadData() - } - - const handleCurrentChange = (page) => { - pagination.page = page - loadData() - } - - // 鍒濆鍖� - onMounted(() => { - loadData() - }) - - return { - loading, - tableData, - searchForm, - pagination, - handleSearch, - handleReset, - handleAdd, - handleEdit, - handleDelete, - handleSizeChange, - handleCurrentChange - } - } -} -</script> - -<style scoped> -.rating-scheme-list { - padding: 20px; -} - -.page-header { - margin-bottom: 20px; -} - -.page-header h2 { - margin: 0; - color: #303133; -} - -.search-bar { - margin-bottom: 20px; - padding: 20px; - background: #fff; - border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - display: flex; - justify-content: flex-end; -} - -.search-form { - display: flex; - align-items: center; - gap: 12px; -} - -.table-container { - background: #fff; - border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.pagination-container { - margin-top: 20px; - text-align: right; -} - -.text-right { - text-align: right; -} -</style> \ No newline at end of file diff --git a/web/src/views/activity/index.vue b/web/src/views/activity-list.vue similarity index 100% rename from web/src/views/activity/index.vue rename to web/src/views/activity-list.vue diff --git a/web/src/views/player/detail.vue b/web/src/views/check-detail.vue similarity index 100% rename from web/src/views/player/detail.vue rename to web/src/views/check-detail.vue diff --git a/web/src/views/player/index.vue b/web/src/views/check-list.vue similarity index 96% rename from web/src/views/player/index.vue rename to web/src/views/check-list.vue index 01b3304..318b58a 100644 --- a/web/src/views/player/index.vue +++ b/web/src/views/check-list.vue @@ -230,9 +230,15 @@ const loadData = async () => { loading.value = true try { + // 纭繚activityId鏄纭殑绫诲瀷 + let activityId = null + if (searchForm.activityId && searchForm.activityId !== '') { + activityId = searchForm.activityId + } + const response = await PlayerApi.getApplications( searchForm.name || '', - searchForm.activityId || null, + activityId, searchForm.state !== '' ? parseInt(searchForm.state) : null, pagination.page, pagination.size diff --git a/web/src/views/employee/EmployeeForm.vue b/web/src/views/employee-detail.vue similarity index 100% rename from web/src/views/employee/EmployeeForm.vue rename to web/src/views/employee-detail.vue diff --git a/web/src/views/employee/index.vue b/web/src/views/employee-list.vue similarity index 80% rename from web/src/views/employee/index.vue rename to web/src/views/employee-list.vue index fcd0387..61201ff 100644 --- a/web/src/views/employee/index.vue +++ b/web/src/views/employee-list.vue @@ -35,11 +35,25 @@ {{ formatDateTime(row.createTime) }} </template> </el-table-column> - <el-table-column label="鎿嶄綔" width="120" fixed="right"> + <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center"> <template #default="{ row }"> <div class="table-actions"> - <el-button type="warning" size="small" @click="handleEdit(row)" :icon="Edit" circle title="缂栬緫"></el-button> - <el-button type="danger" size="small" @click="handleDelete(row)" :icon="Delete" circle title="鍒犻櫎"></el-button> + <el-button + text + :icon="Edit" + size="small" + @click="handleEdit(row)" + class="action-btn edit-btn" + title="缂栬緫" + /> + <el-button + text + :icon="Delete" + size="small" + @click="handleDelete(row)" + class="action-btn delete-btn" + title="鍒犻櫎" + /> </div> </template> </el-table-column> @@ -65,7 +79,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue' import { employeeApi, type Employee } from '@/api/employee' -import EmployeeForm from './EmployeeForm.vue' +import EmployeeForm from './employee-detail.vue' const loading = ref(false) const formVisible = ref(false) @@ -82,10 +96,10 @@ // 鑾峰彇瑙掕壊鏍囩绫诲瀷 const getRoleType = (roleId: string) => { const typeMap: Record<string, string> = { - 'ADMIN': 'danger', - 'MANAGER': 'warning', - 'STAFF': 'primary', - 'REVIEWER': 'success' + 'SUPER_ADMIN': 'danger', + 'AUDITOR': 'warning', + 'JUDGE_MANAGER': 'success', + 'PLAYER': 'primary' } return typeMap[roleId] || 'info' } @@ -93,10 +107,10 @@ // 鑾峰彇瑙掕壊鏂囨湰 const getRoleText = (roleId: string) => { const textMap: Record<string, string> = { - 'ADMIN': '绠$悊鍛�', - 'MANAGER': '缁忕悊', - 'STAFF': '宸ヤ綔浜哄憳', - 'REVIEWER': '瀹℃牳鍛�' + 'SUPER_ADMIN': '瓒呯骇绠$悊鍛�', + 'AUDITOR': '骞冲彴宸ヤ綔浜哄憳', + 'JUDGE_MANAGER': '璇勫', + 'PLAYER': '鍙傝禌浜哄憳' } return textMap[roleId] || roleId } @@ -202,7 +216,27 @@ .table-actions { display: flex; gap: 8px; - flex-wrap: wrap; + justify-content: center; + align-items: center; + flex-wrap: nowrap; // 纭繚涓嶆崲琛� + + .action-btn { + &.edit-btn { + color: #409eff; + + &:hover { + color: #66b1ff; + } + } + + &.delete-btn { + color: #f56c6c; + + &:hover { + color: #f78989; + } + } + } } .pagination { diff --git a/web/src/views/judge/index.vue b/web/src/views/judge-list.vue similarity index 100% rename from web/src/views/judge/index.vue rename to web/src/views/judge-list.vue diff --git a/web/src/views/review/detail.vue b/web/src/views/judge-review-detail.vue similarity index 100% rename from web/src/views/review/detail.vue rename to web/src/views/judge-review-detail.vue diff --git a/web/src/views/review/index.vue b/web/src/views/judge-review-list.vue similarity index 100% rename from web/src/views/review/index.vue rename to web/src/views/judge-review-list.vue diff --git a/web/src/views/competition-promotion/index.vue b/web/src/views/next-list.vue similarity index 100% rename from web/src/views/competition-promotion/index.vue rename to web/src/views/next-list.vue diff --git a/web/src/views/rating/Form.vue b/web/src/views/rating-detail.vue similarity index 100% rename from web/src/views/rating/Form.vue rename to web/src/views/rating-detail.vue diff --git a/web/src/views/rating/index.vue b/web/src/views/rating-list.vue similarity index 100% rename from web/src/views/rating/index.vue rename to web/src/views/rating-list.vue diff --git a/web/src/views/project-review/detail.vue b/web/src/views/review-detail.vue similarity index 100% rename from web/src/views/project-review/detail.vue rename to web/src/views/review-detail.vue diff --git a/web/src/views/project-review/index.vue b/web/src/views/review-list.vue similarity index 100% rename from web/src/views/project-review/index.vue rename to web/src/views/review-list.vue diff --git a/web/test-employee-roles.html b/web/test-employee-roles.html new file mode 100644 index 0000000..a1f8f4b --- /dev/null +++ b/web/test-employee-roles.html @@ -0,0 +1,198 @@ +<!DOCTYPE html> +<html lang="zh-CN"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>鍛樺伐瑙掕壊涓嬫媺妗嗘祴璇�</title> + <style> + body { font-family: Arial, sans-serif; margin: 20px; } + .result { margin: 10px 0; padding: 10px; border: 1px solid #ccc; } + .error { background-color: #ffe6e6; } + .success { background-color: #e6ffe6; } + button { padding: 10px 20px; margin: 5px; } + select { padding: 5px; margin: 5px; min-width: 200px; } + </style> +</head> +<body> + <h1>鍛樺伐瑙掕壊涓嬫媺妗嗘祴璇�</h1> + + <div> + <button onclick="checkAuth()">1. 妫�鏌ヨ璇佺姸鎬�</button> + <button onclick="testRoleAPI()">2. 娴嬭瘯瑙掕壊API</button> + <button onclick="loginAsAdmin()">3. 妯℃嫙绠$悊鍛樼櫥褰�</button> + <button onclick="testRoleAPIAfterLogin()">4. 鐧诲綍鍚庢祴璇曡鑹睞PI</button> + </div> + + <div> + <h3>瑙掕壊涓嬫媺妗嗘ā鎷燂細</h3> + <select id="roleSelect"> + <option value="">璇烽�夋嫨瑙掕壊</option> + </select> + <button onclick="loadRolesIntoSelect()">鍔犺浇瑙掕壊鍒颁笅鎷夋</button> + </div> + + <div id="results"></div> + + <script> + function addResult(content, isError = false) { + const div = document.createElement('div'); + div.className = `result ${isError ? 'error' : 'success'}`; + div.innerHTML = content; + document.getElementById('results').appendChild(div); + } + + function checkAuth() { + const token = localStorage.getItem('auth_token'); + const userInfo = localStorage.getItem('user_info'); + + addResult(` + <strong>璁よ瘉鐘舵��:</strong><br> + Token: ${token ? '瀛樺湪' : '涓嶅瓨鍦�'}<br> + User Info: ${userInfo ? '瀛樺湪' : '涓嶅瓨鍦�'}<br> + ${token ? `Token鍐呭: ${token.substring(0, 50)}...` : ''} + `); + } + + async function testRoleAPI() { + try { + const token = localStorage.getItem('auth_token'); + const headers = { + 'Content-Type': 'application/json' + }; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + const response = await fetch('/api/graphql', { + method: 'POST', + headers, + body: JSON.stringify({ + query: `query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + }` + }) + }); + + const data = await response.json(); + + if (data.errors) { + addResult(`<strong>瑙掕壊API閿欒:</strong><br>${JSON.stringify(data.errors, null, 2)}`, true); + } else { + addResult(`<strong>瑙掕壊API鎴愬姛:</strong><br>瑙掕壊鏁伴噺: ${data.data?.activeRoles?.length || 0}<br>鏁版嵁: <pre>${JSON.stringify(data.data, null, 2)}</pre>`); + } + } catch (error) { + addResult(`<strong>瑙掕壊API璋冪敤澶辫触:</strong><br>${error.message}`, true); + } + } + + async function loginAsAdmin() { + try { + const response = await fetch('/api/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: `mutation Login($phone: String!, $password: String!) { + login(phone: $phone, password: $password) { + token + user { + userId + name + phone + userType + } + } + }`, + variables: { + phone: "13800000001", + password: "123456" + } + }) + }); + + const data = await response.json(); + + if (data.errors) { + addResult(`<strong>鐧诲綍澶辫触:</strong><br>${JSON.stringify(data.errors, null, 2)}`, true); + } else if (data.data?.login?.token) { + localStorage.setItem('auth_token', data.data.login.token); + localStorage.setItem('user_info', JSON.stringify(data.data.login.user)); + addResult(`<strong>鐧诲綍鎴愬姛:</strong><br>Token宸蹭繚瀛�<br>鐢ㄦ埛淇℃伅: ${JSON.stringify(data.data.login.user, null, 2)}`); + } else { + addResult(`<strong>鐧诲綍鍝嶅簲寮傚父:</strong><br>${JSON.stringify(data, null, 2)}`, true); + } + } catch (error) { + addResult(`<strong>鐧诲綍璇锋眰澶辫触:</strong><br>${error.message}`, true); + } + } + + async function testRoleAPIAfterLogin() { + await testRoleAPI(); + } + + async function loadRolesIntoSelect() { + try { + const token = localStorage.getItem('auth_token'); + const headers = { + 'Content-Type': 'application/json' + }; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + const response = await fetch('/api/graphql', { + method: 'POST', + headers, + body: JSON.stringify({ + query: `query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + }` + }) + }); + + const data = await response.json(); + const select = document.getElementById('roleSelect'); + + // 娓呯┖鐜版湁閫夐」锛堥櫎浜嗛粯璁ら�夐」锛� + select.innerHTML = '<option value="">璇烽�夋嫨瑙掕壊</option>'; + + if (data.errors) { + addResult(`<strong>鍔犺浇瑙掕壊鍒颁笅鎷夋澶辫触:</strong><br>${JSON.stringify(data.errors, null, 2)}`, true); + } else if (data.data?.activeRoles) { + const roles = data.data.activeRoles; + roles.forEach(role => { + const option = document.createElement('option'); + option.value = role.id; + option.textContent = role.name; + select.appendChild(option); + }); + addResult(`<strong>鎴愬姛鍔犺浇 ${roles.length} 涓鑹插埌涓嬫媺妗�</strong>`); + } else { + addResult(`<strong>娌℃湁鑾峰彇鍒拌鑹叉暟鎹�</strong>`, true); + } + } catch (error) { + addResult(`<strong>鍔犺浇瑙掕壊鍒颁笅鎷夋澶辫触:</strong><br>${error.message}`, true); + } + } + </script> +</body> +</html> \ No newline at end of file diff --git a/web/test-role-api.html b/web/test-role-api.html new file mode 100644 index 0000000..29433b2 --- /dev/null +++ b/web/test-role-api.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<html> +<head> + <title>娴嬭瘯瑙掕壊API</title> +</head> +<body> + <h1>瑙掕壊API娴嬭瘯</h1> + <button onclick="testRoleAPI()">娴嬭瘯瑙掕壊API</button> + <div id="result"></div> + + <script> + async function testRoleAPI() { + const resultDiv = document.getElementById('result'); + resultDiv.innerHTML = '姝e湪娴嬭瘯...'; + + try { + // 鑾峰彇token锛堝鏋滄湁鐨勮瘽锛� + const token = localStorage.getItem('token'); + + const headers = { + 'Content-Type': 'application/json', + }; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + const query = ` + query GetActiveRoles { + activeRoles { + id + code + name + description + state + createTime + updateTime + } + } + `; + + const response = await fetch('/api/graphql', { + method: 'POST', + headers, + body: JSON.stringify({ + query, + variables: {} + }) + }); + + const result = await response.json(); + + resultDiv.innerHTML = ` + <h3>鍝嶅簲鐘舵��: ${response.status}</h3> + <h3>鍝嶅簲鏁版嵁:</h3> + <pre>${JSON.stringify(result, null, 2)}</pre> + `; + + } catch (error) { + resultDiv.innerHTML = ` + <h3>閿欒:</h3> + <pre>${error.message}</pre> + `; + } + } + </script> +</body> +</html> \ No newline at end of file -- Gitblit v1.8.0