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