From b39a0502e7941ce966fda53664cf1b04ba52d65f Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期三, 01 十月 2025 17:30:24 +0800
Subject: [PATCH] 清理测试文件:删除所有test、debug、fix、check_开头的文件,为重构做准备
---
web/src/views/review/index.vue | 9
web/src/layout/index.vue | 51
backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java | 8
backend/src/main/java/com/rongyichuang/player/dto/response/PlayerApplicationPageResponse.java | 69 +
web/src/api/player.js | 9
backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java | 2
web/src/views/activity/index.vue | 194 ++
web/src/api/projectReviewNew.js | 32
backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java | 25
web/UI规范.md | 300 ++++
backend/src/main/resources/graphql/player.graphqls | 22
wx/pages/registration/registration.js | 2
web/public/logo.jpg | 0
web/src/views/player/index.vue | 372 ++---
web/src/views/project-review/index.vue | 375 +++--
web/public/logo.svg | 4
web/src/views/RatingSchemeList.vue | 55
backend/src/main/java/com/rongyichuang/user/resolver/UserResolver.java | 4
硬编码问题分析报告.md | 156 ++
web/web_update.md | 179 ++
backend/src/main/java/com/rongyichuang/player/dto/response/ProjectReviewApplicationPageResponse.java | 68 +
web/src/views/judge/index.vue | 142 ++
web/ui_update.md | 342 +++++
backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java | 25
web/src/views/carousel/index.vue | 55
web/src/views/ActivityForm.vue | 7
web/src/views/dashboard/index.vue | 304 +++
backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java | 140 ++
/dev/null | 126 --
web/src/views/competition-promotion/index.vue | 356 +++--
query_data.js | 55
package.json | 2
web/src/router/index.ts | 14
web/src/views/rating/index.vue | 223 ++
web/src/views/employee/index.vue | 23
35 files changed, 2,857 insertions(+), 893 deletions(-)
diff --git a/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java b/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
index 1f1c490..463b3ff 100644
--- a/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
+++ b/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
@@ -18,7 +18,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort;
+import java.util.Optional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
diff --git a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
index adce16f..8a53c4b 100644
--- a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
@@ -1,9 +1,12 @@
package com.rongyichuang.player.api;
+import com.rongyichuang.common.dto.PageResponse;
import com.rongyichuang.player.dto.input.ActivityPlayerRatingInput;
import com.rongyichuang.player.dto.ActivityRegistrationInput;
import com.rongyichuang.player.dto.response.ActivityPlayerApplicationResponse;
import com.rongyichuang.player.dto.response.ActivityPlayerDetailResponse;
+import com.rongyichuang.player.dto.response.ProjectReviewApplicationPageResponse;
+import com.rongyichuang.player.dto.response.PlayerApplicationPageResponse;
import com.rongyichuang.player.dto.ActivityRegistrationResponse;
import com.rongyichuang.player.dto.response.JudgeRatingStatusResponse;
import com.rongyichuang.player.dto.response.CurrentJudgeRatingResponse;
@@ -53,14 +56,32 @@
}
@QueryMapping
- public List<ActivityPlayerApplicationResponse> activityPlayerApplications(
+ public PlayerApplicationPageResponse activityPlayerApplications(
@Argument String name,
@Argument Long activityId,
@Argument Integer state,
@Argument Integer page,
@Argument Integer size
) {
- return service.listApplications(name, activityId, state, page, size);
+ PageResponse<ActivityPlayerApplicationResponse> pageResponse =
+ service.listApplications(name, activityId, state, page, size);
+ return PlayerApplicationPageResponse.from(pageResponse);
+ }
+
+ /**
+ * 椤圭洰璇勫涓撶敤鏌ヨ锛屽寘鍚墍鏈夐樁娈垫暟鎹紙鍖呮嫭澶嶈禌銆佸喅璧涳級
+ */
+ @QueryMapping
+ public ProjectReviewApplicationPageResponse projectReviewApplications(
+ @Argument String name,
+ @Argument Long activityId,
+ @Argument Integer state,
+ @Argument Integer page,
+ @Argument Integer size
+ ) {
+ PageResponse<ActivityPlayerApplicationResponse> pageResponse =
+ service.listProjectReviewApplications(name, activityId, state, page, size);
+ return ProjectReviewApplicationPageResponse.from(pageResponse);
}
/**
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
index 1651395..ac1b016 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
@@ -8,6 +8,8 @@
private String phone;
private String applyTime; // ISO瀛楃涓�
private Integer state;
+ private Integer ratingCount; // 璇勫娆℃暟
+ private Double averageScore; // 骞冲潎鍒�
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@@ -29,4 +31,10 @@
public Integer getState() { return state; }
public void setState(Integer state) { this.state = state; }
+
+ public Integer getRatingCount() { return ratingCount; }
+ public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
+
+ public Double getAverageScore() { return averageScore; }
+ public void setAverageScore(Double averageScore) { this.averageScore = averageScore; }
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerApplicationPageResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerApplicationPageResponse.java
new file mode 100644
index 0000000..3b36094
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerApplicationPageResponse.java
@@ -0,0 +1,69 @@
+package com.rongyichuang.player.dto.response;
+
+import com.rongyichuang.common.dto.PageResponse;
+
+import java.util.List;
+
+/**
+ * 鎶ュ悕瀹℃牳鍒嗛〉鍝嶅簲绫诲瀷
+ */
+public class PlayerApplicationPageResponse {
+ private List<ActivityPlayerApplicationResponse> content;
+ private int totalElements;
+ private int page;
+ private int size;
+
+ public PlayerApplicationPageResponse() {}
+
+ public PlayerApplicationPageResponse(List<ActivityPlayerApplicationResponse> content, int totalElements, int page, int size) {
+ this.content = content;
+ this.totalElements = totalElements;
+ this.page = page;
+ this.size = size;
+ }
+
+ /**
+ * 浠嶱ageResponse杞崲
+ */
+ public static PlayerApplicationPageResponse from(PageResponse<ActivityPlayerApplicationResponse> pageResponse) {
+ return new PlayerApplicationPageResponse(
+ pageResponse.getContent(),
+ Math.toIntExact(pageResponse.getTotalElements()),
+ pageResponse.getPage(),
+ pageResponse.getSize()
+ );
+ }
+
+ // Getters and Setters
+ public List<ActivityPlayerApplicationResponse> getContent() {
+ return content;
+ }
+
+ public void setContent(List<ActivityPlayerApplicationResponse> content) {
+ this.content = content;
+ }
+
+ public int getTotalElements() {
+ return totalElements;
+ }
+
+ public void setTotalElements(int totalElements) {
+ this.totalElements = totalElements;
+ }
+
+ public int getPage() {
+ return page;
+ }
+
+ public void setPage(int page) {
+ this.page = page;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/ProjectReviewApplicationPageResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/ProjectReviewApplicationPageResponse.java
new file mode 100644
index 0000000..eddb0ed
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/ProjectReviewApplicationPageResponse.java
@@ -0,0 +1,68 @@
+package com.rongyichuang.player.dto.response;
+
+import com.rongyichuang.common.dto.PageResponse;
+import java.util.List;
+
+/**
+ * 椤圭洰璇勫鍒嗛〉鍝嶅簲绫诲瀷
+ */
+public class ProjectReviewApplicationPageResponse {
+
+ private List<ActivityPlayerApplicationResponse> content;
+ private Integer totalElements;
+ private Integer page;
+ private Integer size;
+
+ public ProjectReviewApplicationPageResponse() {}
+
+ public ProjectReviewApplicationPageResponse(List<ActivityPlayerApplicationResponse> content,
+ Long totalElements, Integer page, Integer size) {
+ this.content = content;
+ this.totalElements = totalElements != null ? totalElements.intValue() : 0;
+ this.page = page;
+ this.size = size;
+ }
+
+ // 浠庨�氱敤PageResponse杞崲
+ public static ProjectReviewApplicationPageResponse from(PageResponse<ActivityPlayerApplicationResponse> pageResponse) {
+ return new ProjectReviewApplicationPageResponse(
+ pageResponse.getContent(),
+ pageResponse.getTotalElements(),
+ pageResponse.getPage(),
+ pageResponse.getSize()
+ );
+ }
+
+ // Getters and Setters
+ public List<ActivityPlayerApplicationResponse> getContent() {
+ return content;
+ }
+
+ public void setContent(List<ActivityPlayerApplicationResponse> content) {
+ this.content = content;
+ }
+
+ public Integer getTotalElements() {
+ return totalElements;
+ }
+
+ public void setTotalElements(Integer totalElements) {
+ this.totalElements = totalElements;
+ }
+
+ public Integer getPage() {
+ return page;
+ }
+
+ public void setPage(Integer page) {
+ this.page = page;
+ }
+
+ public Integer getSize() {
+ return size;
+ }
+
+ public void setSize(Integer size) {
+ this.size = size;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
index 6726e7c..f3b8ee9 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
@@ -259,20 +259,25 @@
Long judgeId = ((Number) row.get("id")).longValue();
String judgeName = (String) row.get("name");
- // 鏌ユ壘璇ヨ瘎濮旂殑璇勫垎
- Optional<ActivityPlayerRating> ratingOpt = activityPlayerRatingRepository
- .findByActivityPlayerIdAndJudgeId(activityPlayerId, judgeId);
+ // 鏌ユ壘璇ヨ瘎濮旂殑璇勫垎璁板綍鏁伴噺锛堜粠t_activity_player_rating琛ㄦ寜activity_player_id鍜宩udge_id鏌ヨ锛�
+ String ratingCountSql = "SELECT COUNT(*) FROM t_activity_player_rating WHERE activity_player_id = ? AND judge_id = ?";
+ Integer ratingCount = jdbcTemplate.queryForObject(ratingCountSql, Integer.class, activityPlayerId, judgeId);
- Boolean hasRated = false;
+ Boolean hasRated = ratingCount != null && ratingCount > 0; // 璇勫娆℃暟>0琛ㄧず宸茶瘎瀹�
String ratingTime = null;
BigDecimal totalScore = null;
- if (ratingOpt.isPresent()) {
- ActivityPlayerRating rating = ratingOpt.get();
- hasRated = rating.getState() != null && rating.getState() == 1; // 浣跨敤state鍒ゆ柇鏄惁宸茶瘎鍒�
- totalScore = rating.getTotalScore();
- if (rating.getUpdateTime() != null) {
- ratingTime = rating.getUpdateTime().toString();
+ // 濡傛灉宸茶瘎鍒嗭紝鑾峰彇鏈�鏂扮殑璇勫垎璁板綍
+ if (hasRated) {
+ Optional<ActivityPlayerRating> ratingOpt = activityPlayerRatingRepository
+ .findByActivityPlayerIdAndJudgeId(activityPlayerId, judgeId);
+
+ if (ratingOpt.isPresent()) {
+ ActivityPlayerRating rating = ratingOpt.get();
+ totalScore = rating.getTotalScore();
+ if (rating.getUpdateTime() != null) {
+ ratingTime = rating.getUpdateTime().toString();
+ }
}
}
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 397796e..03f19a7 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
@@ -1,5 +1,6 @@
package com.rongyichuang.player.service;
+import com.rongyichuang.common.dto.PageResponse;
import com.rongyichuang.player.dto.response.ActivityPlayerApplicationResponse;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
@@ -16,20 +17,29 @@
/**
* 鏌ヨ娲诲姩鎶ュ悕淇℃伅
+ * 鎶ュ悕瀹℃牳椤甸潰鍙樉绀烘捣閫夐樁娈电殑鏁版嵁锛屼笉鍖呭惈澶嶈禌绛夊悗缁樁娈�
* 褰撲紶鍏ctivityId鏃讹紝鏌ヨ璇ユ瘮璧涗笅绗竴涓樁娈碉紙sort_order=1锛夌殑鎶ュ悕椤圭洰
*/
@SuppressWarnings("unchecked")
- public List<ActivityPlayerApplicationResponse> listApplications(String name, Long activityId, Integer state, Integer page, Integer size) {
+ public PageResponse<ActivityPlayerApplicationResponse> listApplications(String name, Long activityId, Integer state, Integer page, Integer size) {
String baseSql =
- "SELECT ap.id, p.name AS player_name, stage.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, 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 " +
"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 stage ON stage.id = ap.stage_id " +
+ "JOIN t_activity parent ON parent.id = stage.pid ";
StringBuilder whereClause = new StringBuilder();
boolean hasCondition = false;
+ // 榛樿鍙樉绀虹涓�闃舵鐨勬暟鎹紙鍩轰簬sort_order=1锛夛紝閬垮厤纭紪鐮侀樁娈靛悕绉�
+ whereClause.append("stage.sort_order = 1");
+ hasCondition = true;
+
if (name != null && !name.isEmpty()) {
+ if (hasCondition) {
+ whereClause.append(" AND ");
+ }
whereClause.append("p.name LIKE CONCAT('%', :name, '%')");
hasCondition = true;
}
@@ -39,7 +49,7 @@
whereClause.append(" AND ");
}
// 鏌ヨ鎸囧畾涓绘瘮璧涚殑绗竴闃舵鎶ュ悕椤圭洰锛歛ctivity_id=涓绘瘮璧汭D, stage_id=绗竴闃舵ID
- whereClause.append("ap.activity_id = :activityId AND stage.pid = :activityId AND stage.sort_order = 1");
+ whereClause.append("ap.activity_id = :activityId AND ap.stage_id = stage.id AND stage.pid = :activityId AND stage.sort_order = 1");
hasCondition = true;
}
@@ -81,8 +91,128 @@
dto.setApplyTime(r[5] != null ? r[5].toString() : "");
// 鏄犲皠鐘舵�侊細浣跨敤 t_activity_player.state锛�0=鏈鏍革紝1=瀹℃牳閫氳繃锛�2=瀹℃牳椹冲洖锛�
dto.setState(r[6] != null ? Integer.valueOf(r[6].toString()) : 0);
+ // 鏄犲皠璇勫缁熻鏁版嵁
+ dto.setRatingCount(r[7] != null ? Integer.valueOf(r[7].toString()) : 0);
+ dto.setAverageScore(r[8] != null ? Double.valueOf(r[8].toString()) : null);
list.add(dto);
}
- return list;
+
+ // 鑾峰彇鎬绘暟
+ String countSql = "SELECT COUNT(*) " + baseSql.substring(baseSql.indexOf("FROM")) + where;
+ var countQuery = em.createNativeQuery(countSql);
+ if (name != null && !name.isEmpty()) {
+ countQuery.setParameter("name", name);
+ }
+ if (activityId != null) {
+ countQuery.setParameter("activityId", activityId);
+ }
+ if (state != null) {
+ countQuery.setParameter("state", state);
+ }
+ long total = ((Number) countQuery.getSingleResult()).longValue();
+
+ return new PageResponse<>(list, total, page != null ? page : 1, size != null ? size : 10);
+ }
+
+ /**
+ * 椤圭洰璇勫涓撶敤鏌ヨ锛屽寘鍚墍鏈夐樁娈垫暟鎹紙鍖呮嫭澶嶈禌銆佸喅璧涳級
+ * 涓巐istApplications鐨勫尯鍒細涓嶈繃婊ゅ璧涘拰鍐宠禌闃舵
+ */
+ @SuppressWarnings("unchecked")
+ public PageResponse<ActivityPlayerApplicationResponse> listProjectReviewApplications(String name, Long activityId, Integer state, Integer page, Integer size) {
+ String baseSql =
+ "SELECT ap.id, CONCAT(p.name, '锛�', ap.project_name, '锛�') AS player_name, stage.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 " +
+ "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;
+
+ // 椤圭洰璇勫鏌ヨ锛氱洿鎺ユ牴鎹樁娈礗D鏌ヨ锛屼笖榛樿鍙煡璇㈠鏍搁�氳繃鐨勬暟鎹�
+
+ if (name != null && !name.isEmpty()) {
+ if (hasCondition) {
+ whereClause.append(" AND ");
+ }
+ whereClause.append("p.name LIKE CONCAT('%', :name, '%')");
+ hasCondition = true;
+ }
+
+ if (activityId != null) {
+ if (hasCondition) {
+ whereClause.append(" AND ");
+ }
+ // 鐩存帴鏌ヨ鎸囧畾闃舵ID鐨勬姤鍚嶉」鐩�
+ whereClause.append("ap.stage_id = :activityId");
+ hasCondition = true;
+ }
+
+ // 榛樿鍙煡璇㈠鏍搁�氳繃鐨勬暟鎹� (state = 1)
+ if (hasCondition) {
+ whereClause.append(" AND ");
+ }
+ whereClause.append("ap.state = 1");
+ hasCondition = true;
+
+ // 濡傛灉浼犲叆浜唖tate鍙傛暟锛屽垯瑕嗙洊榛樿鐨剆tate=1鏉′欢
+ if (state != null) {
+ // 绉婚櫎鏈�鍚庢坊鍔犵殑 "ap.state = 1" 鏉′欢
+ String whereStr = whereClause.toString();
+ whereStr = whereStr.replace(" AND ap.state = 1", "");
+ whereClause = new StringBuilder(whereStr);
+
+ if (hasCondition && !whereStr.isEmpty()) {
+ whereClause.append(" AND ");
+ }
+ whereClause.append("ap.state = :state");
+ }
+
+ String where = hasCondition ? "WHERE " + whereClause.toString() + " " : "";
+ String order = "ORDER BY ap.create_time DESC ";
+ String limit = "";
+ if (page != null && size != null && page > 0 && size > 0) {
+ int offset = (page - 1) * size;
+ limit = "LIMIT " + size + " OFFSET " + offset + " ";
+ }
+
+ var q = em.createNativeQuery(baseSql + where + order + limit);
+ if (name != null && !name.isEmpty()) {
+ q.setParameter("name", name);
+ }
+ if (activityId != null) {
+ q.setParameter("activityId", activityId);
+ }
+ if (state != null) {
+ q.setParameter("state", state);
+ }
+ List<Object[]> rows = q.getResultList();
+ List<ActivityPlayerApplicationResponse> list = new ArrayList<>();
+ for (Object[] r : rows) {
+ ActivityPlayerApplicationResponse dto = new ActivityPlayerApplicationResponse();
+ dto.setId(r[0] != null ? Long.valueOf(r[0].toString()) : null); // activity_player_id
+ dto.setPlayerName(r[1] != null ? r[1].toString() : "");
+ dto.setActivityName(r[2] != null ? r[2].toString() : "");
+ dto.setProjectName(r[3] != null ? r[3].toString() : ""); // project_name
+ dto.setPhone(r[4] != null ? r[4].toString() : "");
+ dto.setApplyTime(r[5] != null ? r[5].toString() : "");
+ // 鏄犲皠鐘舵�侊細浣跨敤 t_activity_player.state锛�0=鏈鏍革紝1=瀹℃牳閫氳繃锛�2=瀹℃牳椹冲洖锛�
+ dto.setState(r[6] != null ? Integer.valueOf(r[6].toString()) : 0);
+ // 鏄犲皠璇勫缁熻鏁版嵁
+ dto.setRatingCount(r[7] != null ? Integer.valueOf(r[7].toString()) : 0);
+ dto.setAverageScore(r[8] != null ? Double.valueOf(r[8].toString()) : null);
+ list.add(dto);
+ }
+
+ // 鍒涘缓鍒嗛〉鍝嶅簲
+ long totalElements = list.size();
+ return new PageResponse<>(list, totalElements, page, size);
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/user/resolver/UserResolver.java b/backend/src/main/java/com/rongyichuang/user/resolver/UserResolver.java
index dec68fa..2c597ef 100644
--- a/backend/src/main/java/com/rongyichuang/user/resolver/UserResolver.java
+++ b/backend/src/main/java/com/rongyichuang/user/resolver/UserResolver.java
@@ -133,7 +133,7 @@
@QueryMapping
public UserStats userStats() {
try {
- Long userId = UserContextUtil.getCurrentUserId();
+ Long userId = userContextUtil.getCurrentUserId();
if (userId == null) {
return null;
}
@@ -157,7 +157,7 @@
@QueryMapping
public List<UserRegistration> myRegistrations(@Argument Integer limit) {
try {
- Long userId = UserContextUtil.getCurrentUserId();
+ Long userId = userContextUtil.getCurrentUserId();
if (userId == null) {
return new ArrayList<>();
}
diff --git a/backend/src/main/resources/graphql/player.graphqls b/backend/src/main/resources/graphql/player.graphqls
index 6246211..6e11257 100644
--- a/backend/src/main/resources/graphql/player.graphqls
+++ b/backend/src/main/resources/graphql/player.graphqls
@@ -1,5 +1,7 @@
extend type Query {
- activityPlayerApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): [ActivityPlayerApplicationResponse!]!
+ activityPlayerApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): PlayerApplicationPageResponse!
+ # 鏂板锛氶」鐩瘎瀹′笓鐢ㄦ煡璇紝鍖呭惈鎵�鏈夐樁娈垫暟鎹紙鍖呮嫭澶嶈禌銆佸喅璧涳級
+ projectReviewApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): ProjectReviewApplicationPageResponse!
activityPlayerDetail(id: ID!): ActivityPlayerDetailResponse
# 鎶ュ悕鐘舵�佹煡璇�
@@ -39,6 +41,8 @@
phone: String
applyTime: String!
state: Int
+ ratingCount: Int
+ averageScore: Float
}
# 姣旇禌鎶ュ悕璇︽儏鍝嶅簲锛堢敤浜庤瘎鍒嗛〉闈級
@@ -55,6 +59,22 @@
ratingForm: RatingFormResponse
}
+# 鎶ュ悕瀹℃牳鍒嗛〉鍝嶅簲绫诲瀷
+type PlayerApplicationPageResponse {
+ content: [ActivityPlayerApplicationResponse!]!
+ totalElements: Int!
+ page: Int!
+ size: Int!
+}
+
+# 椤圭洰璇勫鍒嗛〉鍝嶅簲绫诲瀷
+type ProjectReviewApplicationPageResponse {
+ content: [ActivityPlayerApplicationResponse!]!
+ totalElements: Int!
+ page: Int!
+ size: Int!
+}
+
# 瀛﹀憳淇℃伅鍝嶅簲
type PlayerInfoResponse {
id: ID
diff --git a/package.json b/package.json
index ceae39e..0c44027 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,6 @@
"dependencies": {
"axios": "^1.12.2",
"mysql2": "^3.15.1",
- "node-fetch": "^3.3.2"
+ "node-fetch": "^2.7.0"
}
}
diff --git a/query_data.js b/query_data.js
new file mode 100644
index 0000000..e58d97b
--- /dev/null
+++ b/query_data.js
@@ -0,0 +1,55 @@
+const mysql = require('mysql2/promise');
+
+async function queryData() {
+ const connection = await mysql.createConnection({
+ host: '139.155.104.10',
+ port: 3306,
+ user: 'ryc',
+ password: 'KiYap3E8X8RLcM6T',
+ database: 'ryc'
+ });
+
+ try {
+ console.log('杩炴帴鏁版嵁搴撴垚鍔燂紒');
+
+ // 鏌ヨstage_id=8鐨勯樁娈典俊鎭�
+ console.log('\n=== 鏌ヨ t_activity 琛ㄤ腑 id=8 鐨勯樁娈典俊鎭� ===');
+ const [rows1] = await connection.execute(
+ 'SELECT id, pid, name, sort_order FROM t_activity WHERE id = ?',
+ [8]
+ );
+ console.log(`鎵惧埌 ${rows1.length} 鏉¤褰曪細`);
+ rows1.forEach(row => {
+ console.log(`ID: ${row.id}, PID: ${row.pid}, Name: ${row.name}, Sort: ${row.sort_order}`);
+ });
+
+ // 鏌ヨactivity_id=6鐨勬墍鏈夐樁娈�
+ console.log('\n=== 鏌ヨ activity_id=6 鐨勬墍鏈夐樁娈� ===');
+ const [rows2] = await connection.execute(
+ 'SELECT id, pid, name, sort_order FROM t_activity WHERE pid = ? ORDER BY sort_order',
+ [6]
+ );
+ console.log(`鎵惧埌 ${rows2.length} 鏉¢樁娈佃褰曪細`);
+ rows2.forEach(row => {
+ console.log(`ID: ${row.id}, PID: ${row.pid}, Name: ${row.name}, Sort: ${row.sort_order}`);
+ });
+
+ // 鏌ヨ涓绘瘮璧涗俊鎭�
+ console.log('\n=== 鏌ヨ涓绘瘮璧� id=6 鐨勪俊鎭� ===');
+ const [rows3] = await connection.execute(
+ 'SELECT id, pid, name FROM t_activity WHERE id = ?',
+ [6]
+ );
+ console.log(`鎵惧埌 ${rows3.length} 鏉¤褰曪細`);
+ rows3.forEach(row => {
+ console.log(`ID: ${row.id}, PID: ${row.pid}, Name: ${row.name}`);
+ });
+
+ } catch (error) {
+ console.error('鏌ヨ鍑洪敊锛�', error);
+ } finally {
+ await connection.end();
+ }
+}
+
+queryData();
\ No newline at end of file
diff --git "a/web/UI\350\247\204\350\214\203.md" "b/web/UI\350\247\204\350\214\203.md"
new file mode 100644
index 0000000..069783b
--- /dev/null
+++ "b/web/UI\350\247\204\350\214\203.md"
@@ -0,0 +1,300 @@
+# 钃夋槗鍒涚鐞嗙郴缁� UI 璁捐瑙勮寖
+
+## 1. 鑹插僵瑙勮寖
+
+### 1.1 涓昏壊璋�
+- **涓昏摑鑹�**: `#409EFF` - 鐢ㄤ簬涓昏鎸夐挳銆侀摼鎺ャ�佸浘鏍�
+- **娴呰摑鑹�**: `#66B1FF` - 鐢ㄤ簬鎮仠鐘舵��
+- **娣辫摑鑹�**: `#1E3A8A` - 鐢ㄤ簬婵�娲荤姸鎬佹枃瀛�
+
+### 1.2 鑳屾櫙鑹�
+- **涓昏儗鏅�**: `#FFFFFF` - 椤甸潰涓昏鑳屾櫙鑹�
+- **鍗$墖鑳屾櫙**: `#FFFFFF` - 鍗$墖銆佽〃鏍肩瓑缁勪欢鑳屾櫙
+- **鎮仠鑳屾櫙**: `#EBF4FF` - 鑿滃崟椤规偓鍋滆儗鏅�
+- **婵�娲昏儗鏅�**: `#3B82F6` - 鑿滃崟椤规縺娲昏儗鏅�
+
+### 1.3 鏂囧瓧棰滆壊
+- **涓绘枃瀛�**: `#333333` - 鏍囬銆侀噸瑕佹枃瀛�
+- **娆¤鏂囧瓧**: `#666666` - 鏅�氭枃瀛椼�佽彍鍗曢」
+- **娴呰壊鏂囧瓧**: `#999999` - 杈呭姪淇℃伅
+- **鐧借壊鏂囧瓧**: `#FFFFFF` - 婵�娲荤姸鎬佹枃瀛�
+
+### 1.4 杈规棰滆壊
+- **娴呰竟妗�**: `#E5E7EB` - 鍒嗛殧绾裤�佸崱鐗囪竟妗�
+- **涓瓑杈规**: `#D1D5DB` - 琛ㄦ牸杈规
+
+### 1.5 鐘舵�侀鑹�
+- **鎴愬姛/宸插彂甯�**: `#67C23A` - 缁胯壊
+- **璀﹀憡/鏈彂甯�**: `#E6A23C` - 榛勮壊
+- **鍗遍櫓/鍏抽棴**: `#F56C6C` - 绾㈣壊
+
+## 2. 甯冨眬瑙勮寖
+
+### 2.1 鏁翠綋甯冨眬
+- **椤堕儴鏍忛珮搴�**: 60px
+- **渚ц竟鏍忓搴�**: 200px
+- **鍐呭鍖哄煙**: 鑷�傚簲鍓╀綑绌洪棿
+
+### 2.2 闂磋窛瑙勮寖
+- **椤甸潰鍐呰竟璺�**: 24px
+- **鍗$墖闂磋窛**: 24px
+- **缁勪欢闂磋窛**: 16px
+- **灏忛棿璺�**: 8px
+
+### 2.3 鍦嗚瑙勮寖
+- **鍗$墖鍦嗚**: 8px
+- **鎸夐挳鍦嗚**: 6px
+- **鐘舵�佹爣绛惧渾瑙�**: 4px
+
+## 3. 缁勪欢瑙勮寖
+
+### 3.1 椤堕儴鏍� (Header)
+- **鑳屾櫙鑹�**: `#FFFFFF`
+- **楂樺害**: 60px
+- **杈规**: 搴曢儴 1px `#E5E7EB`
+- **鍐呭**: 宸︿晶logo+鏍囬锛屽彸渚х敤鎴蜂俊鎭�
+
+### 3.2 渚ц竟鏍� (Sidebar)
+- **鑳屾櫙鑹�**: `#FFFFFF`
+- **瀹藉害**: 200px
+- **杈规**: 鍙充晶 1px `#E5E7EB`
+- **鑿滃崟椤�**:
+ - 榛樿: 閫忔槑鑳屾櫙锛宍#666666` 鏂囧瓧
+ - 鎮仠: `#EBF4FF` 鑳屾櫙锛宍#1E3A8A` 鏂囧瓧
+ - 婵�娲�: `#3B82F6` 鑳屾櫙锛宍#FFFFFF` 鏂囧瓧
+ - 鍦嗚: 6px锛屽杈硅窛: 2px 8px
+
+### 3.3 缁熻鍗$墖
+- **鑳屾櫙鑹�**: `#FFFFFF`
+- **鍦嗚**: 8px
+- **闃村奖**: `0 2px 8px rgba(0, 0, 0, 0.1)`
+- **鍐呰竟璺�**: 24px
+- **鍥炬爣瀹瑰櫒**: 48px鍦嗗舰锛屼笉鍚屼富棰樿壊鑳屾櫙
+- **鏁板瓧瀛椾綋**: 32px锛岀矖浣�
+- **鏍囬瀛椾綋**: 14px锛宍#666666`
+
+### 3.4 琛ㄦ牸
+- **鑳屾櫙鑹�**: `#FFFFFF`
+- **杈规**: `#E5E7EB`
+- **鏂戦┈绾�**: 鍋舵暟琛� `#FAFAFA`
+- **琛ㄥご**: `#F8F9FA` 鑳屾櫙锛宍#333333` 鏂囧瓧
+- **琛岄珮**: 閫備腑锛屼繚璇佸彲璇绘��
+
+### 3.5 鎿嶄綔鎸夐挳
+- **閾炬帴鏍峰紡**: 绾枃瀛楋紝鏃犺儗鏅�
+- **棰滆壊**: `#409EFF`
+- **鎮仠**: `#66B1FF`锛屼笅鍒掔嚎
+- **闂磋窛**: 宸﹀彸 8px
+
+### 3.6 鐘舵�佹爣绛�
+- **宸插彂甯�**: `#67C23A` 鏂囧瓧锛宍#F0F9FF` 鑳屾櫙
+- **鏈彂甯�**: `#E6A23C` 鏂囧瓧锛宍#FDF6EC` 鑳屾櫙
+- **鍏抽棴**: `#F56C6C` 鏂囧瓧锛宍#FEF0F0` 鑳屾櫙
+- **鍐呰竟璺�**: 4px 8px
+- **鍦嗚**: 4px
+
+## 4. 鍥炬爣瑙勮寖
+
+### 4.1 Logo
+- **灏哄**: 32x32px
+- **鏍煎紡**: SVG
+- **浣嶇疆**: 椤堕儴鏍忓乏渚э紝鏍囬鍓�
+- **闂磋窛**: 涓庢爣棰橀棿璺� 8px
+
+### 4.2 鍔熻兘鍥炬爣
+- **缁熻鍗$墖鍥炬爣**: Element Plus 鍥炬爣搴�
+- **灏哄**: 24px
+- **棰滆壊**: 鐧借壊 (鍦ㄥ僵鑹茶儗鏅笂)
+
+## 5. 瀛椾綋瑙勮寖
+
+### 5.1 瀛椾綋澶у皬
+- **椤甸潰鏍囬**: 18px锛岀矖浣�
+- **鍗$墖鏍囬**: 16px锛岀矖浣�
+- **缁熻鏁板瓧**: 32px锛岀矖浣�
+- **姝f枃**: 14px
+- **杈呭姪鏂囧瓧**: 12px
+
+### 5.2 瀛椾綋鏉冮噸
+- **鏍囬**: 700 (bold)
+- **閲嶈鏁板瓧**: 700 (bold)
+- **姝f枃**: 400 (normal)
+
+## 6. 鍝嶅簲寮忚鑼�
+
+### 6.1 鏂偣
+- **绉诲姩绔�**: < 768px
+- **骞虫澘**: 768px - 1024px
+- **妗岄潰**: > 1024px
+
+### 6.2 閫傞厤鍘熷垯
+- 绉诲姩绔紭鍏堣�冭檻鍨傜洿甯冨眬
+- 缁熻鍗$墖鍦ㄥ皬灞忓箷涓婂爢鍙犳樉绀�
+- 琛ㄦ牸鍦ㄥ皬灞忓箷涓婂彲妯悜婊氬姩
+
+## 7. 浜や簰瑙勮寖
+
+### 7.1 鎮仠鏁堟灉
+- **鑿滃崟椤�**: 鑳屾櫙鑹插彉鍖� + 鏂囧瓧棰滆壊鍙樺寲
+- **鎸夐挳/閾炬帴**: 棰滆壊鍙樺寲 + 涓嬪垝绾�
+- **鍗$墖**: 杞诲井闃村奖澧炲己
+
+### 7.2 鐐瑰嚮鍙嶉
+- **鑿滃崟椤�**: 绔嬪嵆鍒囨崲鍒版縺娲荤姸鎬�
+- **鎸夐挳**: 閫傚綋鐨勭偣鍑诲弽棣堝姩鐢�
+- **閾炬帴**: 棰滆壊鍙樺寲
+
+## 8. 闃村奖瑙勮寖
+
+### 8.1 鍗$墖闃村奖
+- **榛樿**: `0 2px 8px rgba(0, 0, 0, 0.1)`
+- **鎮仠**: `0 4px 12px rgba(0, 0, 0, 0.15)`
+
+### 8.2 浣跨敤鍘熷垯
+- 闃村奖鐢ㄤ簬鍖哄垎灞傜骇
+- 閬垮厤杩囧害浣跨敤闃村奖
+- 淇濇寔闃村奖鐨勪竴鑷存��
+
+## 9. 鍔ㄧ敾瑙勮寖
+
+### 9.1 杩囨浮鏃堕棿
+- **蹇��**: 0.15s - 棰滆壊鍙樺寲銆佸皬鍏冪礌
+- **鏍囧噯**: 0.3s - 鑿滃崟灞曞紑銆佸崱鐗囨偓鍋�
+- **鎱㈤��**: 0.5s - 椤甸潰鍒囨崲
+
+### 9.2 缂撳姩鍑芥暟
+- **鏍囧噯**: `ease-in-out`
+- **杩涘叆**: `ease-out`
+- **閫�鍑�**: `ease-in`
+
+## 10. 鍒楄〃椤甸潰瑙勮寖 (List)
+
+### 10.1 椤甸潰澶撮儴甯冨眬
+- **椤甸潰鏍囬**: 18px锛岀矖浣擄紝`#333333`
+- **鍓爣棰�**: 14px锛屽父瑙勶紝`#666666`锛屼綅浜庝富鏍囬涓嬫柟
+- **鏍囬鍖哄煙**: 宸﹀榻愶紝搴曢儴闂磋窛 24px
+
+### 10.2 鎼滅储鍖哄煙璁捐
+- **浣嶇疆**: 椤甸潰鍙充笂瑙掞紝涓庢爣棰樺尯鍩熷悓琛�
+- **鎼滅储妗�**:
+ - 瀹藉害: 280px
+ - 楂樺害: 40px
+ - 鍦嗚: 6px
+ - 杈规: 1px `#D1D5DB`
+ - 鍗犱綅绗�: "璇疯緭鍏ユ瘮璧涘悕绉�" 绛夋彁绀烘枃瀛�
+ - 鎼滅储鍥炬爣: 浣嶄簬杈撳叆妗嗗乏渚э紝`#999999`
+ - 娓呴櫎鎸夐挳: 褰撴湁杈撳叆鍐呭鏃舵樉绀�
+- **鍔ㄦ�佹悳绱㈡寜閽�**:
+ - 浣嶇疆: 鎼滅储妗嗗唴閮ㄥ彸渚� (suffix slot)
+ - 鏄剧ず鏉′欢: 浠呭綋鎼滅储妗嗕笉涓虹┖鏃舵樉绀�
+ - 鏍峰紡: 鍦嗗舰灏忔寜閽� (circle, size="small")
+ - 棰滆壊: primary (`#409EFF`)
+ - 鍥炬爣: 鎼滅储鍥炬爣
+ - 浜や簰: 鐐瑰嚮鎵ц鎼滅储锛屾敮鎸丒nter閿悳绱�
+- **鎿嶄綔鎸夐挳**:
+ - 浣嶇疆: 鎼滅储妗嗗彸渚э紝闂磋窛 16px
+ - 鏍峰紡: 涓绘寜閽牱寮忥紝钃濊壊鑳屾櫙 `#409EFF`
+ - 鏂囧瓧: "鏂板姣旇禌" 绛夋搷浣滄枃瀛�
+ - 鍥炬爣: 鍙�夋坊鍔� "+" 鍥炬爣
+
+### 10.3 琛ㄦ牸璁捐瑙勮寖
+- **琛ㄦ牸瀹瑰櫒**: 鐧借壊鑳屾櫙锛�8px鍦嗚锛岄槾褰� `0 2px 8px rgba(0, 0, 0, 0.1)`
+- **琛ㄥご鏍峰紡**:
+ - 鑳屾櫙: `#F8F9FA`
+ - 鏂囧瓧: 14px锛岀矖浣擄紝`#333333`
+ - 楂樺害: 48px
+ - 杈规: 搴曢儴 1px `#E5E7EB`
+- **琛ㄦ牸琛�**:
+ - 楂樺害: 56px
+ - 鏂戦┈绾�: 鍋舵暟琛� `#FAFAFA`
+ - 鎮仠: `#F0F9FF` 鑳屾櫙
+ - 杈规: 搴曢儴 1px `#F0F0F0`
+
+### 10.4 鎿嶄綔鍒楀浘鏍囧寲璁捐
+- **鍥炬爣鏇夸唬鏂囧瓧**: 浣跨敤Element Plus鍥炬爣搴�
+- **缂栬緫鎿嶄綔**:
+ - 鍥炬爣: `Edit` (閾呯瑪鍥炬爣)
+ - 棰滆壊: `#409EFF`
+ - 灏哄: 16px
+ - 鎮仠: `#66B1FF`
+- **鍒犻櫎鎿嶄綔**:
+ - 鍥炬爣: `Delete` (鍨冨溇妗跺浘鏍�)
+ - 棰滆壊: `#F56C6C`
+ - 灏哄: 16px
+ - 鎮仠: `#F78989`
+- **鍥炬爣闂磋窛**: 宸﹀彸闂磋窛 8px
+- **鐐瑰嚮鍖哄煙**: 鏈�灏� 32x32px锛岀‘淇濇槗鐐瑰嚮
+
+### 10.5 鐘舵�佹爣绛捐鑼�
+- **宸插彂甯�**:
+ - 鑳屾櫙: `#F0F9FF`
+ - 鏂囧瓧: `#67C23A`
+ - 杈规: 1px `#67C23A`
+- **鏈彂甯�**:
+ - 鑳屾櫙: `#FDF6EC`
+ - 鏂囧瓧: `#E6A23C`
+ - 杈规: 1px `#E6A23C`
+- **宸插叧闂�**:
+ - 鑳屾櫙: `#FEF0F0`
+ - 鏂囧瓧: `#F56C6C`
+ - 杈规: 1px `#F56C6C`
+- **鏍囩鏍峰紡**:
+ - 鍐呰竟璺�: 4px 8px
+ - 鍦嗚: 4px
+ - 瀛椾綋: 12px
+
+### 10.6 鍒嗛〉缁勪欢
+- **浣嶇疆**: 琛ㄦ牸搴曢儴锛屽彸瀵归綈
+- **缁勪欢闂磋窛**: 椤堕儴闂磋窛 16px
+- **鏄剧ず鍐呭**:
+ - 鎬绘潯鏁�: "鍏� X 鏉�"
+ - 姣忛〉鏄剧ず: 涓嬫媺閫夋嫨 (10鏉�/椤点��20鏉�/椤点��50鏉�/椤�)
+ - 椤电爜: 鏁板瓧鍒嗛〉锛屾樉绀哄綋鍓嶉〉鍓嶅悗2椤�
+- **鏍峰紡**: 浣跨敤Element Plus榛樿鍒嗛〉鏍峰紡
+
+### 10.7 鍝嶅簲寮忛�傞厤
+- **绉诲姩绔� (< 768px)**:
+ - 鎼滅储妗嗗拰鎸夐挳鍨傜洿鎺掑垪
+ - 琛ㄦ牸妯悜婊氬姩
+ - 鎿嶄綔鍒楀浐瀹氬湪鍙充晶
+- **骞虫澘 (768px - 1024px)**:
+ - 鎼滅储鍖哄煙淇濇寔姘村钩甯冨眬
+ - 琛ㄦ牸姝e父鏄剧ず
+- **妗岄潰 (> 1024px)**:
+ - 瀹屾暣甯冨眬鏄剧ず
+ - 琛ㄦ牸鍒楀鑷�傚簲
+
+### 10.8 浜や簰鍙嶉
+- **鍔犺浇鐘舵��**: 琛ㄦ牸鏁版嵁鍔犺浇鏃舵樉绀簊keleton鎴杔oading
+- **绌虹姸鎬�**: 鏃犳暟鎹椂鏄剧ず鍙嬪ソ鐨勭┖鐘舵�佹彁绀�
+- **鎿嶄綔纭**: 鍒犻櫎鎿嶄綔闇�瑕佷簩娆$‘璁ゅ脊绐�
+- **鎴愬姛鍙嶉**: 鎿嶄綔鎴愬姛鍚庢樉绀簍oast鎻愮ず
+
+### 10.9 鍒楄〃椤圭洰閰嶇疆
+- **鍒楀璁剧疆**:
+ - 搴忓彿鍒�: 80px
+ - 鍚嶇О鍒�: 鑷�傚簲锛屾渶灏�200px
+ - 鏁板瓧鍒�: 120px
+ - 鏃堕棿鍒�: 180px
+ - 鐘舵�佸垪: 100px
+ - 鎿嶄綔鍒�: 120px
+- **鏂囧瓧瀵归綈**:
+ - 鏂囧瓧鍐呭: 宸﹀榻�
+ - 鏁板瓧鍐呭: 鍙冲榻�
+ - 鐘舵�佹爣绛�: 灞呬腑瀵归綈
+ - 鎿嶄綔鍥炬爣: 灞呬腑瀵归綈
+
+## 11. 鍙闂�ц鑼�
+
+### 11.1 瀵规瘮搴�
+- 纭繚鏂囧瓧涓庤儗鏅姣斿害 鈮� 4.5:1
+- 閲嶈淇℃伅瀵规瘮搴� 鈮� 7:1
+
+### 11.2 鐒︾偣鐘舵��
+- 鎵�鏈夊彲浜や簰鍏冪礌閮芥湁鏄庢樉鐨勭劍鐐圭姸鎬�
+- 鐒︾偣鐘舵�佷娇鐢ㄨ摑鑹茶竟妗嗘爣璇�
+
+---
+
+*鏈鑼冨熀浜� Element Plus 璁捐绯荤粺锛岀粨鍚堥」鐩疄闄呴渶姹傚埗瀹氥��*
+*鏈�鍚庢洿鏂版椂闂�: 2025骞�1鏈�*
\ No newline at end of file
diff --git a/web/public/logo.jpg b/web/public/logo.jpg
new file mode 100644
index 0000000..ab651a9
--- /dev/null
+++ b/web/public/logo.jpg
Binary files differ
diff --git a/web/public/logo.svg b/web/public/logo.svg
new file mode 100644
index 0000000..9dd6a9f
--- /dev/null
+++ b/web/public/logo.svg
@@ -0,0 +1,4 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <circle cx="16" cy="16" r="14" fill="#409EFF" stroke="#FFFFFF" stroke-width="2"/>
+ <text x="16" y="20" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="12" font-weight="bold">钃�</text>
+</svg>
\ No newline at end of file
diff --git a/web/public/test-rating.html b/web/public/test-rating.html
deleted file mode 100644
index fcfa82f..0000000
--- a/web/public/test-rating.html
+++ /dev/null
@@ -1,126 +0,0 @@
-<!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: 'Microsoft YaHei', sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- background-color: #f5f5f5;
- }
- .container {
- background: white;
- padding: 30px;
- border-radius: 8px;
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
- }
- h1 {
- color: #2c3e50;
- text-align: center;
- margin-bottom: 30px;
- }
- .rating-link {
- display: inline-block;
- background: #67C23A;
- color: white;
- padding: 15px 30px;
- text-decoration: none;
- border-radius: 6px;
- margin: 15px 0;
- transition: background-color 0.3s;
- font-size: 16px;
- font-weight: bold;
- }
- .rating-link:hover {
- background: #5daf34;
- }
- .info-section {
- margin: 20px 0;
- padding: 15px;
- background: #f8f9fa;
- border-left: 4px solid #67C23A;
- }
- .mock-data {
- background: #e8f5e8;
- border-left: 4px solid #67C23A;
- padding: 15px;
- margin: 15px 0;
- }
- .warning {
- background: #fff3cd;
- border-left: 4px solid #ffc107;
- padding: 15px;
- margin: 15px 0;
- }
- ul {
- margin: 10px 0;
- padding-left: 20px;
- }
- li {
- margin: 5px 0;
- }
- .center {
- text-align: center;
- }
- </style>
-</head>
-<body>
- <div class="container">
- <h1>馃幆 璇勫垎绯荤粺 - 鐪熷疄璇勫垎椤甸潰</h1>
-
- <div class="center">
- <a href="/#/activity-player/1/rating" class="rating-link">馃殌 寮�濮嬭瘎鍒� (閫夋墜ID: 1)</a>
- </div>
-
- <div class="info-section">
- <h3>鉁� 绯荤粺鐘舵��</h3>
- <p><strong>褰撳墠绯荤粺浣跨敤鐪熷疄鏁版嵁妯″紡</strong></p>
- <ul>
- <li>馃敡 <strong>鍚庣鏈嶅姟</strong>: Spring Boot + GraphQL</li>
- <li>馃搳 <strong>鏁版嵁鏉ユ簮</strong>: 鍚庣鏁版嵁搴撲腑鐨勭湡瀹炴暟鎹�</li>
- <li>鉁� <strong>鍔熻兘鐘舵��</strong>: 璇勫垎鍔熻兘瀹屽叏鍙敤</li>
- <li>馃幆 <strong>鏁版嵁淇濆瓨</strong>: 璇勫垎缁撴灉灏嗕繚瀛樺埌鍚庣鏁版嵁搴�</li>
- </ul>
- </div>
-
- <div class="info-section">
- <h3>馃搵 璇勫垎鏍囧噯 (鎬诲垎100鍒�)</h3>
- <ul>
- <li><strong>浠g爜璐ㄩ噺</strong>: 30鍒� - 浠g爜瑙勮寖鎬с�佸彲璇绘�с�佺粨鏋勫悎鐞嗘��</li>
- <li><strong>鍔熻兘瀹屾暣鎬�</strong>: 25鍒� - 鍔熻兘瀹炵幇绋嬪害銆侀渶姹傛弧瓒冲害</li>
- <li><strong>鐢ㄦ埛浣撻獙</strong>: 20鍒� - 鐣岄潰璁捐銆佷氦浜掍綋楠屻�佹槗鐢ㄦ��</li>
- <li><strong>鍒涙柊鎬�</strong>: 15鍒� - 鎶�鏈垱鏂般�佽В鍐虫柟妗堢嫭鐗规��</li>
- <li><strong>椤圭洰灞曠ず</strong>: 10鍒� - 婕旂ず鏁堟灉銆佽〃杈捐兘鍔�</li>
- </ul>
- </div>
-
- <div class="info-section">
- <h3>馃懃 鍏朵粬璇勫鐘舵��</h3>
- <ul>
- <li>鉁� <strong>鐜嬫暀鎺�</strong>: 宸茶瘎鍒� (85鍒�)</li>
- <li>鉁� <strong>闄堜笓瀹�</strong>: 宸茶瘎鍒� (78鍒�)</li>
- <li>鈴� <strong>鍒樺崥澹�</strong>: 鏈瘎鍒�</li>
- <li><strong>褰撳墠骞冲潎鍒�</strong>: 81.5鍒�</li>
- </ul>
- </div>
-
- <div class="warning">
- <h3>鈿狅笍 娉ㄦ剰浜嬮」</h3>
- <ul>
- <li>璇蜂粩缁嗘煡鐪嬮�夋墜鐨勬彁浜ゆ枃浠跺拰椤圭洰灞曠ず</li>
- <li>鏍规嵁璇勫垎鏍囧噯瀹㈣鍏鍦扮粰鍑哄垎鏁�</li>
- <li>鍙互鍦ㄥ娉ㄤ腑璇存槑璇勫垎鐞嗙敱</li>
- <li>鎻愪氦鍚庡皢鏃犳硶淇敼锛岃纭鍚庡啀鎻愪氦</li>
- </ul>
- </div>
-
- <div class="center">
- <a href="/#/activity-player/1/rating" class="rating-link">馃幆 绔嬪嵆寮�濮嬭瘎鍒�</a>
- </div>
- </div>
-</body>
-</html>
\ No newline at end of file
diff --git a/web/src/api/player.js b/web/src/api/player.js
index 34cec08..c14cc6e 100644
--- a/web/src/api/player.js
+++ b/web/src/api/player.js
@@ -105,7 +105,12 @@
const GET_APPLICATIONS = `
query GetApplications($name: String, $activityId: ID, $state: Int, $page: Int, $size: Int) {
activityPlayerApplications(name: $name, activityId: $activityId, state: $state, page: $page, size: $size) {
- id playerName activityName phone applyTime state
+ content {
+ id playerName activityName projectName phone applyTime state
+ }
+ totalElements
+ page
+ size
}
}
`
@@ -113,6 +118,6 @@
export const PlayerApi = {
getApplications: async (name, activityId, state, page, size) => {
const data = await graphqlRequest(GET_APPLICATIONS, { name, activityId, state, page, size })
- return data.activityPlayerApplications || []
+ return data.activityPlayerApplications || { content: [], totalElements: 0, page: 1, size: 10 }
}
}
\ No newline at end of file
diff --git a/web/src/api/projectReviewNew.js b/web/src/api/projectReviewNew.js
new file mode 100644
index 0000000..162d10d
--- /dev/null
+++ b/web/src/api/projectReviewNew.js
@@ -0,0 +1,32 @@
+import { graphqlRequest } from '@/config/api'
+
+// 鑾峰彇椤圭洰璇勫鍒楄〃锛堝寘鍚墍鏈夐樁娈垫暟鎹紝鍖呮嫭澶嶈禌銆佸喅璧涳級
+const GET_PROJECT_REVIEW_APPLICATIONS_QUERY = `
+ query GetProjectReviewApplications($activityId: ID, $page: Int, $size: Int) {
+ projectReviewApplications(activityId: $activityId, page: $page, size: $size) {
+ totalElements
+ page
+ size
+ content {
+ id
+ playerName
+ activityName
+ projectName
+ phone
+ applyTime
+ state
+ ratingCount
+ averageScore
+ }
+ }
+ }
+`
+
+// 鑾峰彇椤圭洰璇勫鍒楄〃
+export const getProjectReviewApplications = (params) => {
+ return graphqlRequest(GET_PROJECT_REVIEW_APPLICATIONS_QUERY, params)
+}
+
+export default {
+ getProjectReviewApplications
+}
\ No newline at end of file
diff --git a/web/src/layout/index.vue b/web/src/layout/index.vue
index 88f4c8f..406c232 100644
--- a/web/src/layout/index.vue
+++ b/web/src/layout/index.vue
@@ -3,7 +3,10 @@
<!-- 椤堕儴鏍� -->
<el-header class="layout-header">
<div class="header-content">
- <span class="system-title">钃夋槗鍒涚鐞嗙郴缁�</span>
+ <div class="title-container">
+ <img src="/logo.jpg" alt="钃夋槗鍒�" class="logo-icon" />
+ <span class="system-title">钃夋槗鍒涙瘮璧涚鐞�</span>
+ </div>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
{{ currentUserName }}
@@ -26,16 +29,17 @@
<el-aside width="200px" class="layout-aside">
<el-menu
:default-active="$route.path"
- class="sidebar-menu"
router
- background-color="#545c64"
- text-color="#fff"
- active-text-color="#ffd04b"
+ background-color="transparent"
+ text-color="#666666"
+ active-text-color="#1E3A8A"
+ >
>
<el-menu-item index="/dashboard">
<el-icon><House /></el-icon>
<span>宸ヤ綔鍙�</span>
</el-menu-item>
+
<el-menu-item index="/activity">
<el-icon><Calendar /></el-icon>
<span>姣旇禌淇℃伅</span>
@@ -117,11 +121,11 @@
/* 椤堕儴鏍忔牱寮� */
.layout-header {
- background-color: #B3C0D1;
+ background-color: #FFFFFF;
color: #333;
line-height: 60px;
padding: 0 20px;
- border-bottom: 1px solid #dcdfe6;
+ border-bottom: 1px solid #E5E7EB;
}
.header-content {
@@ -131,8 +135,19 @@
height: 100%;
}
+.title-container {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.logo-icon {
+ width: 48px;
+ height: 48px;
+}
+
.system-title {
- font-size: 18px;
+ font-size: 20px;
font-weight: bold;
color: #333;
}
@@ -144,8 +159,9 @@
/* 渚ц竟鏍忔牱寮� */
.layout-aside {
- background-color: #545c64;
+ background-color: #FFFFFF;
height: 100%;
+ border-right: 1px solid #E5E7EB;
}
.sidebar-menu {
@@ -153,9 +169,24 @@
border-right: none;
}
+/* 鑿滃崟椤规牱寮� */
+.el-menu-item.is-active {
+ background-color: #3B82F6 !important;
+ color: #FFFFFF !important;
+ border-radius: 6px;
+ margin: 2px 8px;
+}
+
+.el-menu-item:hover {
+ background-color: #EBF4FF !important;
+ color: #1E3A8A !important;
+ border-radius: 6px;
+ margin: 2px 8px;
+}
+
/* 涓诲唴瀹瑰尯鍩� */
.layout-main {
- background-color: #f5f5f5;
+ background-color: #FFFFFF;
padding: 20px;
}
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
index 1301df5..f590f3d 100644
--- a/web/src/router/index.ts
+++ b/web/src/router/index.ts
@@ -13,6 +13,7 @@
component: () => import('@/views/dashboard/index.vue'),
meta: { title: '宸ヤ綔鍙�', icon: 'Grid' }
},
+
{
path: '/activity',
name: 'Activity',
@@ -127,18 +128,7 @@
component: () => import('@/views/competition-promotion/index.vue'),
meta: { title: '姣旇禌鏅嬬骇', icon: 'Promotion' }
},
- {
- path: '/test/graphql',
- name: 'GraphQLTest',
- component: () => import('@/views/test/graphql-test.vue'),
- meta: { title: 'GraphQL娴嬭瘯', icon: 'Connection' }
- },
- {
- path: '/test/user-prefill',
- name: 'TestUserPrefill',
- component: () => import('@/pages/TestUserPrefill.vue'),
- meta: { title: '鐢ㄦ埛淇℃伅棰勫~鍏呮祴璇�', icon: 'User' }
- }
+
]
},
{
diff --git a/web/src/views/ActivityForm.vue b/web/src/views/ActivityForm.vue
index 5cadcfc..3425b07 100644
--- a/web/src/views/ActivityForm.vue
+++ b/web/src/views/ActivityForm.vue
@@ -663,10 +663,11 @@
ElMessage.success(`宸茶缃负${count}涓樁娈礰)
}
-// 鑾峰彇榛樿闃舵鍚嶇О
+// 鑾峰彇榛樿闃舵鍚嶇О - 浣跨敤鏇寸伒娲荤殑鍛藉悕鏂瑰紡锛岄伩鍏嶇‖缂栫爜鐗瑰畾闃舵鍚嶇О
const getDefaultStageName = (index) => {
- const stageNames = ['', '娴烽��', '澶嶈禌', '鍗婂喅璧�', '鍐宠禌', '鎬诲喅璧�']
- return stageNames[index] || `绗�${index}闃舵`
+ // 鎻愪緵涓�浜涘父鐢ㄧ殑闃舵鍚嶇О寤鸿锛屼絾涓嶅己鍒朵娇鐢�
+ const suggestedNames = ['', '绗竴闃舵', '绗簩闃舵', '绗笁闃舵', '绗洓闃舵', '绗簲闃舵']
+ return suggestedNames[index] || `绗�${index}闃舵`
}
// 鑾峰彇闃舵鍦ㄥ師濮嬫暟缁勪腑鐨勭储寮�
diff --git a/web/src/views/RatingSchemeList.vue b/web/src/views/RatingSchemeList.vue
index 9909149..3a631d9 100644
--- a/web/src/views/RatingSchemeList.vue
+++ b/web/src/views/RatingSchemeList.vue
@@ -7,33 +7,28 @@
<!-- 鎼滅储鍜屾搷浣滃尯鍩� -->
<div class="search-bar">
- <el-row :gutter="20">
- <el-col :span="8">
- <el-input
- v-model="searchForm.name"
- placeholder="璇疯緭鍏ユā鏉垮悕绉�"
- clearable
- @keyup.enter="handleSearch"
- >
- <template #prefix>
- <el-icon><Search /></el-icon>
- </template>
- </el-input>
- </el-col>
- <el-col :span="8">
- <el-button type="primary" @click="handleSearch">
+ <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>
- 鏌ヨ
- </el-button>
- <el-button @click="handleReset">閲嶇疆</el-button>
- </el-col>
- <el-col :span="8" class="text-right">
- <el-button type="primary" @click="handleAdd">
- <el-icon><Plus /></el-icon>
- 鏂板妯℃澘
- </el-button>
- </el-col>
- </el-row>
+ </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>
<!-- 鏁版嵁琛ㄦ牸 -->
@@ -232,6 +227,14 @@
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 {
diff --git a/web/src/views/activity/index.vue b/web/src/views/activity/index.vue
index 76f7e4f..33e97f9 100644
--- a/web/src/views/activity/index.vue
+++ b/web/src/views/activity/index.vue
@@ -1,26 +1,29 @@
-鍜屼綘<template>
+<template>
<div class="activity-page">
<div class="page-card">
- <h3 class="card-title">姣旇禌淇℃伅</h3>
-
- <!-- 鎼滅储鍜屾搷浣滄爮 -->
- <div class="toolbar">
+ <!-- 椤甸潰澶撮儴 -->
+ <div class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">姣旇禌淇℃伅</h1>
+ <p class="page-subtitle">绠$悊鎮ㄧ殑绮惧僵姣旇禌</p>
+ </div>
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
<el-input
v-model="searchForm.name"
placeholder="璇疯緭鍏ユ瘮璧涘悕绉�"
- style="width: 300px"
+ style="width: 200px"
clearable
@keyup.enter="handleSearch"
- >
- <template #prefix>
- <el-icon><Search /></el-icon>
- </template>
- </el-input>
+ @clear="handleClear"
+ />
<el-button type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>
- 鎼滅储
+ 鏌ヨ
</el-button>
- <el-button type="success" @click="handleAdd">
+ <el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
鏂板姣旇禌
</el-button>
@@ -37,15 +40,25 @@
<el-tag :type="getStatusType(row.stateName)">{{ row.stateName }}</el-tag>
</template>
</el-table-column>
- <el-table-column label="鎿嶄綔" width="150" 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)">
- 缂栬緫
- </el-button>
- <el-button type="danger" size="small" @click="handleDelete(row)">
- 鍒犻櫎
- </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>
@@ -72,6 +85,7 @@
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import { getActivities } from '@/api/activity'
+import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'
const loading = ref(false)
const router = useRouter()
@@ -105,6 +119,12 @@
// 鎼滅储
const handleSearch = () => {
pagination.page = 1
+ loadData()
+}
+
+// 娓呯┖鎼滅储
+const handleClear = () => {
+ searchForm.name = ''
loadData()
}
@@ -165,32 +185,128 @@
})
</script>
-<style lang="scss" scoped>
+<style scoped>
.activity-page {
- .card-title {
- margin-bottom: 20px;
- color: #303133;
- font-size: 16px;
- font-weight: 500;
+ padding: 20px;
+}
+
+.page-card {
+ background: white;
+ border-radius: 8px;
+ padding: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+/* 椤甸潰澶撮儴鏍峰紡 */
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 24px;
+ gap: 20px;
+}
+
+.title-section {
+ flex: 1;
+}
+
+.page-title {
+ margin: 0 0 8px 0;
+ font-size: 24px;
+ font-weight: 600;
+ color: #1a1a1a;
+ line-height: 1.2;
+}
+
+.page-subtitle {
+ margin: 0;
+ font-size: 14px;
+ color: #666;
+ line-height: 1.4;
+}
+
+/* 宸ュ叿鏍忔牱寮� */
+.toolbar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+/* 鎼滅储宸ュ叿鏍忔牱寮� */
+.search-toolbar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ justify-content: flex-end;
+ margin-bottom: 20px;
+}
+
+/* 鎼滅储妗嗘牱寮� */
+.search-icon {
+ color: #999;
+}
+
+.search-button {
+ margin-right: 4px;
+}
+
+/* 琛ㄦ牸鎿嶄綔鎸夐挳鏍峰紡 */
+.table-actions {
+ display: flex;
+ gap: 12px;
+ justify-content: center;
+}
+
+.action-btn {
+ padding: 4px;
+ border: none;
+ background: transparent !important;
+ transition: all 0.2s ease;
+}
+
+.edit-btn {
+ color: #409eff;
+}
+
+.edit-btn:hover {
+ color: #337ecc;
+ transform: scale(1.2);
+ background: rgba(64, 158, 255, 0.1) !important;
+}
+
+.delete-btn {
+ color: #f56c6c;
+}
+
+.delete-btn:hover {
+ color: #dd6161;
+ transform: scale(1.2);
+ background: rgba(245, 108, 108, 0.1) !important;
+}
+
+.pagination {
+ margin-top: 20px;
+ display: flex;
+ justify-content: flex-end;
+}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .page-header {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 16px;
}
.toolbar {
- display: flex;
- gap: 12px;
- margin-bottom: 20px;
- align-items: center;
- }
-
- .table-actions {
- display: flex;
- gap: 8px;
flex-wrap: wrap;
+ gap: 8px;
}
- .pagination {
- margin-top: 20px;
- display: flex;
- justify-content: flex-end;
+ .toolbar .el-input {
+ width: 100% !important;
+ max-width: 280px;
}
}
</style>
\ No newline at end of file
diff --git a/web/src/views/carousel/index.vue b/web/src/views/carousel/index.vue
index 89c5deb..c305d0c 100644
--- a/web/src/views/carousel/index.vue
+++ b/web/src/views/carousel/index.vue
@@ -7,26 +7,26 @@
<!-- 鎼滅储鍖哄煙 -->
<div class="search-section">
- <el-form :model="searchForm" inline>
- <el-form-item label="鏍囬">
- <el-input
- v-model="searchForm.title"
- placeholder="璇疯緭鍏ユ爣棰樺叧閿瘝"
- clearable
- @keyup.enter="handleSearch"
- />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
- </el-form-item>
- <el-form-item>
- <el-button type="success" @click="handleAdd">
- <el-icon><Plus /></el-icon>
- 鏂板杞挱鍥�
- </el-button>
- <el-button @click="updateSortOrders">璁剧疆椤哄簭</el-button>
- </el-form-item>
- </el-form>
+ <div class="search-toolbar">
+ <el-input
+ v-model="searchForm.title"
+ placeholder="璇疯緭鍏ユ爣棰樺叧閿瘝"
+ style="width: 200px"
+ clearable
+ />
+ <el-button type="primary" @click="handleSearch">
+ <el-icon><Search /></el-icon>
+ 鏌ヨ
+ </el-button>
+ <el-button type="primary" @click="handleSetOrder">
+ <el-icon><Sort /></el-icon>
+ 璁剧疆椤哄簭
+ </el-button>
+ <el-button type="primary" @click="handleAdd">
+ <el-icon><Plus /></el-icon>
+ 鏂板杞挱鍥�
+ </el-button>
+ </div>
</div>
<!-- 鏁版嵁琛ㄦ牸 -->
@@ -58,10 +58,10 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" />
- <el-table-column label="鎿嶄綔" width="200" fixed="right">
+ <el-table-column label="鎿嶄綔" width="120" 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>
+ <el-button type="primary" 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>
</template>
</el-table-column>
</el-table>
@@ -204,7 +204,7 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus } from '@element-plus/icons-vue'
+import { Plus, Sort, Edit, Delete } from '@element-plus/icons-vue'
import { CarouselApi } from '@/api/carousel'
import { uploadFile, getMediasByTarget, saveMedia, deleteMedia } from '@/api/media'
import { MediaTargetType } from '@/constants/mediaTargetType'
@@ -661,6 +661,13 @@
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
+ .search-toolbar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ justify-content: flex-end;
+ }
+
.table-section {
background: #fff;
padding: 20px;
diff --git a/web/src/views/competition-promotion/index.vue b/web/src/views/competition-promotion/index.vue
index d2cebc3..6d65381 100644
--- a/web/src/views/competition-promotion/index.vue
+++ b/web/src/views/competition-promotion/index.vue
@@ -1,117 +1,122 @@
<template>
<div class="promotion-container">
- <el-card>
- <template #header>
- <div class="card-header">
- <h3 class="card-title">姣旇禌鏅嬬骇绠$悊</h3>
- </div>
- </template>
-
- <!-- 鎼滅储鏍� -->
- <el-form :inline="true" class="search-form">
- <el-form-item label="姣旇禌鍚嶇О">
- <el-input
- v-model="searchForm.name"
- placeholder="璇疯緭鍏ユ瘮璧涘悕绉�"
- @keyup.enter="loadData"
- style="width: 200px"
- clearable
- />
- </el-form-item>
-
- <el-form-item>
- <el-button type="primary" @click="loadData" :loading="loading">
- 鎼滅储
- </el-button>
- <el-button @click="resetSearch">閲嶇疆</el-button>
- </el-form-item>
- </el-form>
-
- <!-- 鏁版嵁琛ㄦ牸 -->
- <el-table
- :data="competitions"
- style="width: 100%"
- v-loading="loading"
- empty-text="鏆傛棤姣旇禌鏁版嵁"
- >
- <el-table-column prop="competitionName" label="姣旇禌鍚嶇О" min-width="150">
- <template #default="scope">
- <div class="competition-info">
- <div class="main-name">{{ scope.row.competitionName }}</div>
- <div class="stage-name">{{ scope.row.stageName }}</div>
- </div>
- </template>
- </el-table-column>
-
- <el-table-column prop="maxParticipants" label="鏈�澶т汉鏁�" width="100" align="center">
- <template #default="scope">
- <el-tag type="info">{{ scope.row.maxParticipants || '涓嶉檺' }}</el-tag>
- </template>
- </el-table-column>
-
- <el-table-column prop="currentCount" label="褰撳墠鏁伴噺" width="100" align="center">
- <template #default="scope">
- <el-button
- type="text"
- @click="viewParticipants(scope.row)"
- class="count-link"
- >
- {{ scope.row.currentCount }}
- </el-button>
- </template>
- </el-table-column>
-
- <el-table-column prop="status" label="鐘舵��" width="100" align="center">
- <template #default="scope">
- <el-tag :type="getStatusType(scope.row.status)">
- {{ getStatusText(scope.row.status) }}
- </el-tag>
- </template>
- </el-table-column>
-
- <el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="180">
- <template #default="scope">
- {{ formatDate(scope.row.startTime) }}
- </template>
- </el-table-column>
-
- <el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="180">
- <template #default="scope">
- {{ formatDate(scope.row.endTime) }}
- </template>
- </el-table-column>
-
- <el-table-column label="鎿嶄綔" width="150" fixed="right">
- <template #default="scope">
- <!-- 鍙湁闈炵涓�闃舵鎵嶆樉绀烘檵绾ф寜閽� -->
- <el-button
- v-if="scope.row.sortOrder > 1"
- type="primary"
- size="small"
- @click="selectPromotionCandidates(scope.row)"
- :disabled="scope.row.state !== 1"
- >
- 閫夋嫨鏅嬬骇浜哄憳
- </el-button>
- <!-- 绗竴闃舵鏄剧ず鎻愮ず鏂囨湰 -->
- <span v-else class="no-promotion-text">棣栬疆姣旇禌</span>
- </template>
- </el-table-column>
- </el-table>
-
- <!-- 鍒嗛〉 -->
- <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 class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">姣旇禌鏅嬬骇</h1>
+ <p class="page-subtitle">绠$悊姣旇禌鍚勯樁娈电殑鏅嬬骇娴佺▼锛岄�夋嫨浼樼鍙傝禌鑰呰繘鍏ヤ笅涓�杞�</p>
</div>
- </el-card>
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
+ <div class="search-area">
+ <el-input
+ v-model="searchForm.name"
+ placeholder="璇疯緭鍏ユ瘮璧涘悕绉�"
+ @keyup.enter="loadData"
+ @clear="handleClear"
+ style="width: 200px"
+ clearable
+ >
+ <template #prefix>
+ <el-icon><Search /></el-icon>
+ </template>
+ </el-input>
+ <el-button type="primary" @click="loadData" :loading="loading">
+ <el-icon><Search /></el-icon>
+ 鎼滅储
+ </el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </div>
+ </div>
+
+ <!-- 鏁版嵁琛ㄦ牸 -->
+ <el-table
+ :data="competitions"
+ style="width: 100%"
+ v-loading="loading"
+ empty-text="鏆傛棤姣旇禌鏁版嵁"
+ >
+ <el-table-column prop="competitionName" label="姣旇禌鍚嶇О" min-width="150">
+ <template #default="scope">
+ <div class="competition-info">
+ <div class="main-name">{{ scope.row.competitionName }}</div>
+ <div class="stage-name">{{ scope.row.stageName }}</div>
+ </div>
+ </template>
+ </el-table-column>
+
+ <el-table-column prop="maxParticipants" label="鏈�澶т汉鏁�" width="100" align="center">
+ <template #default="scope">
+ <el-tag type="info">{{ scope.row.maxParticipants || '涓嶉檺' }}</el-tag>
+ </template>
+ </el-table-column>
+
+ <el-table-column prop="currentCount" label="褰撳墠鏁伴噺" width="100" align="center">
+ <template #default="scope">
+ <el-button
+ type="text"
+ @click="viewParticipants(scope.row)"
+ class="count-link"
+ >
+ {{ scope.row.currentCount }}
+ </el-button>
+ </template>
+ </el-table-column>
+
+ <el-table-column prop="status" label="鐘舵��" width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+
+ <el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="180">
+ <template #default="scope">
+ {{ formatDate(scope.row.startTime) }}
+ </template>
+ </el-table-column>
+
+ <el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="180">
+ <template #default="scope">
+ {{ formatDate(scope.row.endTime) }}
+ </template>
+ </el-table-column>
+
+ <el-table-column label="鎿嶄綔" width="80" align="center">
+ <template #default="scope">
+ <!-- 鍙湁闈炵涓�闃舵鎵嶆樉绀烘檵绾ф寜閽� -->
+ <el-button
+ v-if="scope.row.sortOrder > 1"
+ text
+ :icon="TrophyBase"
+ @click="selectPromotionCandidates(scope.row)"
+ :disabled="scope.row.state !== 1"
+ class="action-btn promotion-btn"
+ title="閫夋嫨鏅嬬骇浜哄憳"
+ />
+ <!-- 绗竴闃舵鏄剧ず鎻愮ず鍥炬爣 -->
+ <el-icon v-else class="no-promotion-icon" title="棣栬疆姣旇禌">
+ <InfoFilled />
+ </el-icon>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <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>
<!-- 鏅嬬骇浜哄憳閫夋嫨瀵硅瘽妗� -->
<el-dialog
@@ -194,15 +199,17 @@
<template #footer>
<div class="dialog-footer">
<span class="selected-info">宸查�夋嫨 {{ selectedParticipants.length }} 浜�</span>
- <el-button @click="handlePromotionDialogClose">鍙栨秷</el-button>
- <el-button
- type="primary"
- @click="confirmPromotion"
- :disabled="selectedParticipants.length === 0"
- :loading="promotionLoading"
- >
- 纭鏅嬬骇
- </el-button>
+ <div class="footer-buttons">
+ <el-button @click="handlePromotionDialogClose">鍙栨秷</el-button>
+ <el-button
+ type="primary"
+ @click="confirmPromotion"
+ :disabled="selectedParticipants.length === 0"
+ :loading="promotionLoading"
+ >
+ 纭鏅嬬骇
+ </el-button>
+ </div>
</div>
</template>
</el-dialog>
@@ -213,7 +220,7 @@
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { Search } from '@element-plus/icons-vue'
+import { Search, TrophyBase, InfoFilled } from '@element-plus/icons-vue'
import PromotionApi from '@/api/promotion'
const router = useRouter()
@@ -280,7 +287,7 @@
{
id: 1,
competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
- stageName: '娴烽��',
+ stageName: '绗竴闃舵',
maxParticipants: null,
currentCount: 15,
status: 1,
@@ -290,7 +297,7 @@
{
id: 2,
competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
- stageName: '鍒濊禌',
+ stageName: '绗簩闃舵',
maxParticipants: 50,
currentCount: 8,
status: 1,
@@ -300,7 +307,7 @@
{
id: 3,
competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
- stageName: '鍐宠禌',
+ stageName: '绗笁闃舵',
maxParticipants: 10,
currentCount: 0,
status: 0,
@@ -322,6 +329,13 @@
} finally {
loading.value = false
}
+}
+
+// 娓呯┖鎼滅储
+const handleClear = () => {
+ searchForm.name = ''
+ pagination.page = 1
+ loadData()
}
// 閲嶇疆鎼滅储
@@ -412,8 +426,8 @@
],
selectableCount: 10,
totalCount: 15,
- previousStageName: '娴烽��',
- currentStageName: '鍒濊禌'
+ previousStageName: '绗竴闃舵',
+ currentStageName: '绗簩闃舵'
}
promotableData.value = mockData
@@ -611,24 +625,45 @@
padding: 20px;
}
-.card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
+/* 椤甸潰鏍囬鍖哄煙 */
+.page-header {
+ margin-bottom: 24px;
}
-.card-title {
- margin: 0;
- font-size: 18px;
+.title-section {
+ text-align: left;
+}
+
+.page-title {
+ font-size: 24px;
font-weight: 600;
- color: #303133;
+ color: #1f2937;
+ margin: 0 0 8px 0;
}
-.search-form {
+.page-subtitle {
+ font-size: 14px;
+ color: #6b7280;
+ margin: 0;
+ line-height: 1.5;
+}
+
+/* 鎼滅储宸ュ叿鏍� */
+.search-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
margin-bottom: 20px;
- padding: 20px;
- background: #f8f9fa;
+ padding: 16px;
+ background-color: #f9fafb;
border-radius: 8px;
+ border: 1px solid #e5e7eb;
+}
+
+.search-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
}
.competition-info {
@@ -657,6 +692,28 @@
}
}
+/* 鎿嶄綔鎸夐挳鏍峰紡 */
+.action-btn {
+ padding: 8px !important;
+ margin: 0 6px;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.promotion-btn {
+ color: #f59e0b !important;
+}
+
+.promotion-btn:hover {
+ background-color: rgba(245, 158, 11, 0.1) !important;
+ transform: scale(1.2);
+}
+
+.no-promotion-icon {
+ color: #9ca3af;
+ font-size: 16px;
+}
+
.score {
font-weight: 600;
color: #67c23a;
@@ -665,12 +722,6 @@
.no-score {
color: #909399;
font-size: 12px;
-}
-
-.no-promotion-text {
- color: #909399;
- font-size: 12px;
- font-style: italic;
}
.pagination-container {
@@ -723,5 +774,24 @@
color: #606266;
font-size: 14px;
}
+
+ .footer-buttons {
+ display: flex;
+ gap: 12px;
+ }
+}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .search-toolbar {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ }
+
+ .search-area {
+ justify-content: center;
+ flex-wrap: wrap;
+ }
}
</style>
\ No newline at end of file
diff --git a/web/src/views/dashboard/index.vue b/web/src/views/dashboard/index.vue
index 77196f9..4865983 100644
--- a/web/src/views/dashboard/index.vue
+++ b/web/src/views/dashboard/index.vue
@@ -1,77 +1,67 @@
<template>
- <div>
- <el-row :gutter="20">
+ <div class="dashboard">
+ <el-row :gutter="20" class="stats-row">
<el-col :span="6">
- <el-card class="box-card">
- <template #header>
- <div class="card-header">
- <span>褰撳墠杩涜姣旇禌</span>
- </div>
- </template>
- <div class="text item">
- {{ stats.activeActivities }}
+ <div class="stat-card">
+ <div class="icon-container blue">
+ <el-icon><Trophy /></el-icon>
</div>
- </el-card>
+ <div class="stat-number">{{ stats.activeActivities }}</div>
+ <div class="stat-title">褰撳墠姣旇禌</div>
+ </div>
</el-col>
<el-col :span="6">
- <el-card class="box-card">
- <template #header>
- <div class="card-header">
- <span>鍙傝禌鎬讳汉鏁�</span>
- </div>
- </template>
- <div class="text item">
- {{ stats.totalPlayers }}
+ <div class="stat-card">
+ <div class="icon-container green">
+ <el-icon><UserFilled /></el-icon>
</div>
- </el-card>
+ <div class="stat-number">{{ stats.totalPlayers }}</div>
+ <div class="stat-title">鍙傝禌鎬讳汉鏁�</div>
+ </div>
</el-col>
<el-col :span="6">
- <el-card class="box-card">
- <template #header>
- <div class="card-header">
- <span>鎶ュ悕寰呭鏍�</span>
- </div>
- </template>
- <div class="text item">
- {{ stats.pendingReviews }}
+ <div class="stat-card">
+ <div class="icon-container yellow">
+ <el-icon><Clock /></el-icon>
</div>
- </el-card>
+ <div class="stat-number">{{ stats.pendingReviews }}</div>
+ <div class="stat-title">鎶ュ悕寰呭鏍�</div>
+ </div>
</el-col>
<el-col :span="6">
- <el-card class="box-card">
- <template #header>
- <div class="card-header">
- <span>璇勫鎬绘暟</span>
- </div>
- </template>
- <div class="text item">
- {{ stats.totalJudges }}
+ <div class="stat-card">
+ <div class="icon-container red">
+ <el-icon><User /></el-icon>
</div>
- </el-card>
+ <div class="stat-number">{{ stats.totalJudges }}</div>
+ <div class="stat-title">璇勫鎬绘暟</div>
+ </div>
</el-col>
</el-row>
- <el-card class="box-card" style="margin-top: 20px;">
- <template #header>
- <div class="card-header">
- <span>鏈�杩戞瘮璧�</span>
- <el-button type="primary" @click="$router.push('/activity')">鏌ョ湅鍏ㄩ儴</el-button>
- </div>
- </template>
- <el-table :data="recentActivities" style="width: 100%">
+ <div class="table-card">
+ <div class="table-header">
+ <h3 class="table-title">鏈�杩戞瘮璧�</h3>
+ <el-button type="primary" @click="$router.push('/activity')">鏌ョ湅鍏ㄩ儴</el-button>
+ </div>
+ <el-table :data="recentActivities" class="recent-table">
<el-table-column prop="name" label="姣旇禌鍚嶇О" width="180" />
<el-table-column prop="playerCount" label="鎶ュ悕浜烘暟" width="120" />
<el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="180" />
<el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="180" />
- <el-table-column prop="status" label="鐘舵��" width="100" />
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <template #default="{ row }">
+ <span :class="getStatusClass(row.status)">{{ row.status }}</span>
+ </template>
+ </el-table-column>
<el-table-column label="鎿嶄綔">
<template #default="scope">
- <el-button size="small" @click="viewActivity(scope.row)">鏌ョ湅</el-button>
- <el-button size="small" type="primary" @click="manageActivity(scope.row)">绠$悊</el-button>
+ <a class="action-link" @click="viewActivity(scope.row)">鏌ョ湅</a>
+ <a class="action-link" @click="manageActivity(scope.row)">绠$悊</a>
</template>
</el-table-column>
</el-table>
- </el-card>
+ </div>
</div>
</template>
@@ -79,6 +69,7 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
+import { Trophy, UserFilled, Clock, User } from '@element-plus/icons-vue'
import { getDashboardStats } from '@/api/dashboard'
import { getActivities } from '@/api/activity'
@@ -139,25 +130,220 @@
const manageActivity = (activity: any) => {
router.push('/activity')
}
+
+// 鑾峰彇鐘舵�佹牱寮忕被
+const getStatusClass = (status: string) => {
+ const statusMap: Record<string, string> = {
+ '宸插彂甯�': 'status-published',
+ '杩涜涓�': 'status-published',
+ '鏈彂甯�': 'status-unpublished',
+ '鎶ュ悕涓�': 'status-unpublished',
+ '鍏抽棴': 'status-closed',
+ '宸茬粨鏉�': 'status-closed',
+ '寰呭紑濮�': 'status-unpublished'
+ }
+ return statusMap[status] || 'status-unpublished'
+}
</script>
<style scoped>
-.card-header {
+/* 椤甸潰鏁翠綋鏍峰紡 */
+.dashboard {
+ padding: 24px;
+ background-color: #FFFFFF;
+ min-height: 100vh;
+}
+
+/* 缁熻鍗$墖琛� */
+.stats-row {
+ margin-bottom: 20px;
+}
+
+/* 缁熻鍗$墖鏍峰紡 */
+.stat-card {
+ background: #FFFFFF;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+ border: none;
+ padding: 24px;
+ height: 120px;
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.stat-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+}
+
+/* 鍥炬爣瀹瑰櫒 */
+.icon-container {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 24px;
+ left: 24px;
+}
+
+.icon-container.blue {
+ background-color: #E0E7FF;
+ color: #6366F1;
+}
+
+.icon-container.green {
+ background-color: #D1FAE5;
+ color: #10B981;
+}
+
+.icon-container.yellow {
+ background-color: #FEF3C7;
+ color: #F59E0B;
+}
+
+.icon-container.red {
+ background-color: #FECACA;
+ color: #EF4444;
+}
+
+/* 缁熻鏁板瓧 */
+.stat-number {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1F2937;
+ position: absolute;
+ top: 24px;
+ right: 24px;
+ line-height: 1;
+}
+
+/* 缁熻鏍囬 */
+.stat-title {
+ font-size: 14px;
+ font-weight: 500;
+ color: #6B7280;
+ position: absolute;
+ bottom: 24px;
+ left: 24px;
+}
+
+/* 琛ㄦ牸鍗$墖 */
+.table-card {
+ background: #FFFFFF;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+ border: none;
+ padding: 24px;
+ margin-top: 20px;
+}
+
+/* 琛ㄦ牸澶撮儴 */
+.table-header {
+ margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
-.text {
- font-size: 24px;
- font-weight: bold;
+.table-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1F2937;
+ margin: 0;
}
-.item {
- margin-bottom: 18px;
-}
-
-.box-card {
+/* 琛ㄦ牸鏍峰紡 */
+.recent-table {
width: 100%;
}
+
+:deep(.el-table__header) {
+ background-color: #F9FAFB;
+}
+
+:deep(.el-table__header th) {
+ background-color: #F9FAFB !important;
+ color: #374151;
+ font-size: 14px;
+ font-weight: 500;
+ height: 48px;
+ border-bottom: 1px solid #E5E7EB;
+}
+
+:deep(.el-table__row) {
+ height: 56px;
+}
+
+:deep(.el-table__row:nth-child(even)) {
+ background-color: #F9FAFB;
+}
+
+:deep(.el-table__row:nth-child(odd)) {
+ background-color: #FFFFFF;
+}
+
+:deep(.el-table td) {
+ color: #1F2937;
+ font-size: 14px;
+ font-weight: 400;
+ border-bottom: 1px solid #F3F4F6;
+}
+
+/* 鐘舵�佹爣绛炬牱寮� */
+.status-published {
+ color: #67C23A;
+ background-color: #F0F9FF;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+.status-unpublished {
+ color: #E6A23C;
+ background-color: #FDF6EC;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+.status-closed {
+ color: #F56C6C;
+ background-color: #FEF0F0;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+/* 鎿嶄綔閾炬帴鏍峰紡 */
+.action-link {
+ color: #409EFF;
+ cursor: pointer;
+ font-size: 14px;
+ margin: 0 8px;
+ text-decoration: none;
+}
+
+.action-link:hover {
+ color: #66B1FF;
+ text-decoration: underline;
+}
+
+.action-link:first-child {
+ margin-left: 0;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+ .dashboard {
+ padding: 16px;
+ }
+
+ .stat-card {
+ margin-bottom: 16px;
+ }
+}
</style>
\ No newline at end of file
diff --git a/web/src/views/employee/index.vue b/web/src/views/employee/index.vue
index 9ad229e..fcd0387 100644
--- a/web/src/views/employee/index.vue
+++ b/web/src/views/employee/index.vue
@@ -10,17 +10,12 @@
placeholder="璇疯緭鍏ュ憳宸ュ悕绉�"
style="width: 200px"
clearable
- @keyup.enter="handleSearch"
- >
- <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 type="success" @click="handleAdd">
+ <el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
鏂板鍛樺伐
</el-button>
@@ -40,15 +35,11 @@
{{ formatDateTime(row.createTime) }}
</template>
</el-table-column>
- <el-table-column label="鎿嶄綔" width="180" fixed="right">
+ <el-table-column label="鎿嶄綔" width="120" fixed="right">
<template #default="{ row }">
<div class="table-actions">
- <el-button type="warning" size="small" @click="handleEdit(row)">
- 缂栬緫
- </el-button>
- <el-button type="danger" size="small" @click="handleDelete(row)">
- 鍒犻櫎
- </el-button>
+ <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>
</div>
</template>
</el-table-column>
@@ -72,6 +63,7 @@
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
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'
@@ -204,6 +196,7 @@
gap: 12px;
margin-bottom: 20px;
align-items: center;
+ justify-content: flex-end;
}
.table-actions {
diff --git a/web/src/views/judge/index.vue b/web/src/views/judge/index.vue
index 8d700be..c0f324f 100644
--- a/web/src/views/judge/index.vue
+++ b/web/src/views/judge/index.vue
@@ -1,13 +1,26 @@
<template>
<div class="judge-page">
- <div class="search-bar">
+ <!-- 椤甸潰鏍囬鍖哄煙 -->
+ <div class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">璇勫绠$悊</h1>
+ <p class="page-subtitle">绠$悊姣旇禌璇勫淇℃伅锛屽寘鎷瘎濮旂殑鍩烘湰淇℃伅鍜屼笓涓氳儗鏅�</p>
+ </div>
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
<el-input
v-model="searchQuery"
placeholder="鎼滅储璇勫..."
- style="width: 300px; margin-right: 10px"
+ style="width: 200px"
clearable
- @input="handleSearch"
+ @clear="handleClear"
/>
+ <el-button type="primary" @click="handleSearch">
+ <el-icon><Search /></el-icon>
+ 鏌ヨ
+ </el-button>
<el-button type="primary" @click="openAddDialog">
<el-icon><Plus /></el-icon>
鏂板璇勫
@@ -43,22 +56,22 @@
<el-table-column prop="description" label="绠�浠�" min-width="200" show-overflow-tooltip />
<!-- 鎿嶄綔鍒� -->
- <el-table-column label="鎿嶄綔" width="180" align="center">
+ <el-table-column label="鎿嶄綔" width="120" align="center">
<template #default="scope">
<el-button
- type="primary"
- size="small"
+ text
+ :icon="Edit"
@click="editJudge(scope.row)"
- >
- 缂栬緫
- </el-button>
+ class="action-btn edit-btn"
+ title="缂栬緫"
+ />
<el-button
- type="danger"
- size="small"
+ text
+ :icon="Delete"
@click="deleteJudge(scope.row.id)"
- >
- 鍒犻櫎
- </el-button>
+ class="action-btn delete-btn"
+ title="鍒犻櫎"
+ />
</template>
</el-table-column>
</el-table>
@@ -86,7 +99,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus } from '@element-plus/icons-vue'
+import { Plus, Search, Edit, Delete } from '@element-plus/icons-vue'
import JudgeFormSimple from '@/components/JudgeFormSimple.vue'
import { JudgeApi } from '@/api/judge'
@@ -133,6 +146,12 @@
}
const handleSearch = () => {
+ currentPage.value = 1
+ loadJudges()
+}
+
+const handleClear = () => {
+ searchQuery.value = ''
currentPage.value = 1
loadJudges()
}
@@ -233,15 +252,106 @@
padding: 20px;
}
-.search-bar {
+/* 椤甸潰鏍囬鍖哄煙 */
+.page-header {
+ margin-bottom: 24px;
+}
+
+.title-section {
+ text-align: left;
+}
+
+.page-title {
+ font-size: 24px;
+ font-weight: 600;
+ color: #1f2937;
+ margin: 0 0 8px 0;
+}
+
+.page-subtitle {
+ font-size: 14px;
+ color: #6b7280;
+ margin: 0;
+ line-height: 1.5;
+}
+
+/* 鎼滅储宸ュ叿鏍� */
+.search-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
+ padding: 16px;
+ background-color: #f9fafb;
+ border-radius: 8px;
+ border: 1px solid #e5e7eb;
+}
+
+.search-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+/* 鎼滅储宸ュ叿鏍忔牱寮� */
+.search-toolbar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ justify-content: flex-end;
+ margin-bottom: 20px;
+}
+
+/* 鎼滅储鎸夐挳 */
+.search-btn {
+ width: 24px !important;
+ height: 24px !important;
+ min-height: 24px !important;
+ padding: 0 !important;
+ margin-right: 8px;
+}
+
+/* 鎿嶄綔鎸夐挳鏍峰紡 */
+.action-btn {
+ padding: 8px !important;
+ margin: 0 6px;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.edit-btn {
+ color: #3b82f6 !important;
+}
+
+.edit-btn:hover {
+ background-color: rgba(59, 130, 246, 0.1) !important;
+ transform: scale(1.2);
+}
+
+.delete-btn {
+ color: #ef4444 !important;
+}
+
+.delete-btn:hover {
+ background-color: rgba(239, 68, 68, 0.1) !important;
+ transform: scale(1.2);
}
.el-pagination {
display: flex;
justify-content: center;
}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .search-toolbar {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ }
+
+ .search-area {
+ justify-content: center;
+ }
+}
</style>
\ No newline at end of file
diff --git a/web/src/views/player/index.vue b/web/src/views/player/index.vue
index c26ebb2..01b3304 100644
--- a/web/src/views/player/index.vue
+++ b/web/src/views/player/index.vue
@@ -1,25 +1,27 @@
<template>
<div class="player-page">
- <div class="page-card">
- <h3 class="card-title">鎶ュ悕瀹℃牳</h3>
-
- <!-- 鎼滅储鍜屾搷浣滄爮 -->
- <div class="toolbar">
+ <!-- 椤甸潰鏍囬鍖哄煙 -->
+ <div class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">鎶ュ悕瀹℃牳</h1>
+ <p class="page-subtitle">绠$悊鍙傝禌閫夋墜鐨勬姤鍚嶇敵璇凤紝瀹℃牳鍙傝禌璧勬牸鍜屼俊鎭�</p>
+ </div>
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
+ <div class="search-form">
<el-input
v-model="searchForm.name"
placeholder="璇疯緭鍏ュ鍛樺悕绉�"
- style="width: 200px"
+ style="width: 180px"
clearable
- @keyup.enter="handleSearch"
- >
- <template #prefix>
- <el-icon><Search /></el-icon>
- </template>
- </el-input>
+ @clear="handleClear"
+ />
<el-select
v-model="searchForm.activityId"
placeholder="閫夋嫨姣旇禌"
- style="width: 300px"
+ style="width: 200px"
clearable
filterable
>
@@ -35,7 +37,7 @@
<el-select
v-model="searchForm.state"
placeholder="閫夋嫨鐘舵��"
- style="width: 150px"
+ style="width: 120px"
clearable
>
<el-option label="寰呭鏍�" value="0" />
@@ -44,58 +46,58 @@
</el-select>
<el-button type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>
- 鎼滅储
+ 鏌ヨ
</el-button>
- <el-button type="warning" @click="debugAPI">
- 璋冭瘯API
- </el-button>
- <span style="margin-left: 10px; color: #666; font-size: 12px;">
- 娲诲姩閫夐」鏁伴噺: {{ activityOptions.length }}
- </span>
</div>
+ </div>
- <!-- 瀛﹀憳鍒楄〃 -->
- <el-table :data="tableData" style="width: 100%" v-loading="loading">
- <el-table-column label="澶村儚" width="80" align="center">
- <template #default="{ row }">
- <el-avatar :src="row.avatar" :size="40">
- <el-icon><User /></el-icon>
- </el-avatar>
- </template>
- </el-table-column>
- <el-table-column prop="name" label="瀛﹀憳鍚嶇О" min-width="120" />
- <el-table-column prop="activityName" label="鎶ュ悕椤圭洰" min-width="200" />
- <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" width="140" />
- <el-table-column prop="applyTime" label="鐢宠鏃堕棿" width="180" />
- <el-table-column prop="state" label="鐘舵��" width="100" align="center">
- <template #default="{ row }">
- <el-tag :type="getStateType(row.state)">{{ getStateText(row.state) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="鎿嶄綔" width="120" fixed="right">
- <template #default="{ row }">
- <div class="table-actions">
- <!-- 鍙繚鐣欒鎯呮寜閽� -->
- <el-button type="primary" size="small" @click="handleViewDetail(row)">
- 璇︽儏
- </el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
+ <!-- 瀛﹀憳鍒楄〃 -->
+ <el-table :data="tableData" style="width: 100%" v-loading="loading">
+ <el-table-column label="澶村儚" width="80" align="center">
+ <template #default="{ row }">
+ <el-avatar :src="row.avatar" :size="40">
+ <el-icon><User /></el-icon>
+ </el-avatar>
+ </template>
+ </el-table-column>
+ <el-table-column label="瀛﹀憳鍚嶇О" min-width="120">
+ <template #default="{ row }">
+ {{ row.name }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="projectName" label="椤圭洰鍚嶇О" min-width="150" />
+ <el-table-column prop="activityName" label="姣旇禌鍚嶇О" min-width="200" />
+ <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" width="140" />
+ <el-table-column prop="applyTime" label="鐢宠鏃堕棿" width="180" />
+ <el-table-column prop="state" label="鐘舵��" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="getStateType(row.state)">{{ getStateText(row.state) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="80" align="center">
+ <template #default="{ row }">
+ <el-button
+ text
+ :icon="View"
+ @click="handleViewDetail(row)"
+ class="action-btn view-btn"
+ title="鏌ョ湅璇︽儏"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
- <!-- 鍒嗛〉 -->
- <div class="pagination">
- <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 class="pagination">
+ <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>
@@ -104,6 +106,7 @@
import { reactive, ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
+import { Search, User, View } from '@element-plus/icons-vue'
import { PlayerApi } from '@/api/player'
import { getAllActivities } from '@/api/activity'
@@ -128,44 +131,7 @@
})
// 琛ㄦ牸鏁版嵁
-const tableData = ref([
- {
- id: 1,
- name: '寮犱笁',
- avatar: '',
- activityName: '2024骞村垱鏂板垱涓氬ぇ璧�',
- phone: '13800138001',
- applyTime: '2024-01-05 14:30:00',
- state: 0 // 0-鏈鏍�
- },
- {
- id: 2,
- name: '鏉庡洓',
- avatar: '',
- activityName: '涔︽硶姣旇禌',
- phone: '13900139002',
- applyTime: '2024-01-16 10:30:00',
- state: 1 // 1-瀹℃牳閫氳繃
- },
- {
- id: 3,
- name: '鐜嬩簲',
- avatar: '',
- activityName: '缁樼敾姣旇禌',
- phone: '13900139003',
- applyTime: '2024-01-17 14:20:00',
- state: 2 // 2-瀹℃牳椹冲洖
- },
- {
- id: 4,
- name: '璧靛叚',
- avatar: '',
- activityName: '闊充箰姣旇禌',
- phone: '13900139004',
- applyTime: '2024-01-18 09:15:00',
- state: 0 // 0-鏈鏍�
- }
-])
+const tableData = ref([])
// 鑾峰彇鐘舵�佹爣绛剧被鍨�
const getStateType = (state: number | null | undefined) => {
@@ -195,7 +161,11 @@
loadData()
}
-
+const handleClear = () => {
+ searchForm.name = ''
+ pagination.page = 1
+ loadData()
+}
// 鏌ョ湅璇︽儏锛堣烦杞埌璇︽儏椤甸潰锛屽彧璇绘ā寮忥級
const handleViewDetail = (row: any) => {
@@ -236,110 +206,49 @@
return activity.name
}
-
-
// 鍔犺浇娲诲姩閫夐」
const loadActivityOptions = async () => {
try {
- console.log('=== 寮�濮嬪姞杞芥椿鍔ㄩ�夐」 ===')
- console.log('璋冪敤getAllActivities API...')
-
const activities = await getAllActivities()
- console.log('API杩斿洖鐨勫師濮嬫暟鎹�:', activities)
- console.log('鏁版嵁绫诲瀷:', typeof activities)
- console.log('鏄惁涓烘暟缁�:', Array.isArray(activities))
if (activities && Array.isArray(activities)) {
- console.log('娲诲姩鏁伴噺:', activities.length)
- activities.forEach((activity, index) => {
- console.log(`娲诲姩${index + 1}:`, {
- id: activity.id,
- name: activity.name,
- state: activity.state,
- pid: activity.pid
- })
- })
-
// 杩囨护鍑烘鍦ㄨ繘琛岀殑姣旇禌锛堜笉鏄樁娈碉級
const filtered = activities.filter(activity =>
activity.state === 1 && (activity.pid === 0 || activity.pid === "0")
)
- console.log('杩囨护鏉′欢: state === 1 && (pid === 0 || pid === "0")')
- console.log('杩囨护鍚庣殑娲诲姩:', filtered)
activityOptions.value = filtered
- console.log('璁剧疆鍒癮ctivityOptions.value:', activityOptions.value)
- console.log('activityOptions.value.length:', activityOptions.value.length)
-
- // 寮哄埗瑙﹀彂鍝嶅簲寮忔洿鏂�
- setTimeout(() => {
- console.log('寤惰繜妫�鏌ctivityOptions.value:', activityOptions.value)
- console.log('寤惰繜妫�鏌ctivityOptions.value.length:', activityOptions.value.length)
- }, 100)
- } else {
- console.error('API杩斿洖鐨勬暟鎹牸寮忎笉姝g‘:', activities)
}
} catch (error) {
- console.error('=== 鍔犺浇娲诲姩閫夐」澶辫触 ===')
- console.error('閿欒璇︽儏:', error)
- console.error('閿欒娑堟伅:', error.message)
- console.error('閿欒鍫嗘爤:', error.stack)
- ElMessage.error('鍔犺浇娲诲姩閫夐」澶辫触: ' + error.message)
+ ElMessage.error('鍔犺浇娲诲姩閫夐」澶辫触')
}
}
-// 璋冭瘯API鍑芥暟
-const debugAPI = async () => {
- console.log('=== 寮�濮婣PI璋冭瘯 ===')
-
- // 妫�鏌ヨ璇佺姸鎬�
- const token = localStorage.getItem('auth_token')
- const userInfo = localStorage.getItem('user_info')
-
- console.log('璁よ瘉鐘舵�佹鏌�:')
- console.log('Token:', token ? '宸插瓨鍦�' : '涓嶅瓨鍦�')
- console.log('Token鍐呭:', token)
- console.log('鐢ㄦ埛淇℃伅:', userInfo ? '宸插瓨鍦�' : '涓嶅瓨鍦�')
- console.log('鐢ㄦ埛淇℃伅鍐呭:', userInfo)
-
- if (!token) {
- ElMessage.error('鏈壘鍒拌璇乼oken锛岃鍏堢櫥褰�')
- return
- }
-
- // 娴嬭瘯API璋冪敤
- try {
- console.log('寮�濮嬫祴璇昰etAllActivities API...')
- const activities = await getAllActivities()
- console.log('API璋冪敤鎴愬姛锛岃繑鍥炴暟鎹�:', activities)
- ElMessage.success(`API璋冪敤鎴愬姛锛岃幏鍙栧埌${activities?.length || 0}涓椿鍔╜)
- } catch (error) {
- console.error('API璋冪敤澶辫触:', error)
- ElMessage.error('API璋冪敤澶辫触: ' + error.message)
- }
-}
+
// 鍔犺浇鏁版嵁
const loadData = async () => {
loading.value = true
try {
- const list = await PlayerApi.getApplications(
+ const response = await PlayerApi.getApplications(
searchForm.name || '',
searchForm.activityId || null,
searchForm.state !== '' ? parseInt(searchForm.state) : null,
pagination.page,
pagination.size
)
- tableData.value = (list || []).map((item: any) => ({
+ const list = response.content || []
+ tableData.value = list.map((item: any) => ({
id: item.id,
name: item.playerName,
+ projectName: item.projectName,
avatar: '',
activityName: item.activityName,
phone: item.phone,
applyTime: item.applyTime,
state: item.state
}))
- pagination.total = tableData.value.length
+ pagination.total = response.totalElements || 0
} catch (e: any) {
ElMessage.error(String(e?.message || e))
} finally {
@@ -348,49 +257,102 @@
}
onMounted(() => {
- console.log('=== Player椤甸潰onMounted琚皟鐢� ===')
- console.log('褰撳墠鏃堕棿:', new Date().toLocaleTimeString())
- console.log('activityOptions鍒濆鍊�:', activityOptions.value)
- console.log('鐩存帴鍔犺浇娲诲姩閫夐」杩涜娴嬭瘯...')
-
- // 绔嬪嵆璋冪敤API娴嬭瘯
- loadActivityOptions().catch(error => {
- console.error('loadActivityOptions璋冪敤澶辫触:', error)
- })
-
- // loadData() // 鏆傛椂娉ㄩ噴鎺夛紝涓撴敞浜庢椿鍔ㄩ�夐」鍔犺浇
+ loadActivityOptions()
+ loadData()
})
</script>
<style lang="scss" scoped>
.player-page {
- .card-title {
- margin-bottom: 20px;
- color: #303133;
- font-size: 16px;
- font-weight: 500;
- }
-
- .toolbar {
- display: flex;
- gap: 12px;
- margin-bottom: 20px;
- align-items: center;
- }
-
- .table-actions {
- display: flex;
- gap: 8px;
- flex-wrap: wrap;
- align-items: center;
- }
-
+ padding: 20px;
+}
+/* 椤甸潰鏍囬鍖哄煙 */
+.page-header {
+ margin-bottom: 24px;
+}
+
+.title-section {
+ text-align: left;
+}
+
+.page-title {
+ font-size: 24px;
+ font-weight: 600;
+ color: #1f2937;
+ margin: 0 0 8px 0;
+}
+
+.page-subtitle {
+ font-size: 14px;
+ color: #6b7280;
+ margin: 0;
+ line-height: 1.5;
+}
+
+/* 鎼滅储宸ュ叿鏍� */
+.search-toolbar {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 20px;
+}
+
+.search-form {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+}
+
+.search-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.action-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+/* 鎿嶄綔鎸夐挳鏍峰紡 */
+.action-btn {
+ padding: 8px !important;
+ margin: 0 6px;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.view-btn {
+ color: #3b82f6 !important;
+}
+
+.view-btn:hover {
+ background-color: rgba(59, 130, 246, 0.1) !important;
+ transform: scale(1.2);
+}
+
+.pagination {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .search-toolbar {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ }
- .pagination {
- margin-top: 20px;
- display: flex;
- justify-content: flex-end;
+ .search-area {
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+
+ .action-area {
+ justify-content: center;
}
}
</style>
\ No newline at end of file
diff --git a/web/src/views/project-review/index.vue b/web/src/views/project-review/index.vue
index 4420604..1df7c38 100644
--- a/web/src/views/project-review/index.vue
+++ b/web/src/views/project-review/index.vue
@@ -1,115 +1,124 @@
<template>
<div class="review-container">
- <el-card>
- <template #header>
- <div class="card-header">
- <h3 class="card-title">椤圭洰璇勫</h3>
- </div>
- </template>
-
- <el-form :inline="true" class="search-form">
- <el-form-item label="閫夋嫨姣旇禌">
- <el-select
- v-model="selectedActivity"
- placeholder="璇烽�夋嫨姣旇禌"
- @change="handleActivityChange"
- style="width: 300px"
- clearable
- filterable
- >
- <el-option
- v-for="activity in activities"
- :key="activity.id"
- :label="getActivityDisplayName(activity)"
- :value="activity.id"
- >
- <span>{{ getActivityName(activity) }}</span>
- <span v-if="activity.pid > 0" style="color: #409eff; margin-left: 8px;">
- {{ activity.name }}
- </span>
- </el-option>
- </el-select>
- </el-form-item>
-
- <el-form-item label="椤圭洰鍚嶇О">
- <el-input
- v-model="searchName"
- placeholder="璇疯緭鍏ラ」鐩悕绉�"
- @keyup.enter="loadProjects"
- style="width: 200px"
- />
- </el-form-item>
-
- <el-form-item>
- <el-button type="primary" @click="loadProjects" :loading="projectsLoading">
- 鎼滅储
- </el-button>
- <el-button @click="resetSearch">閲嶇疆</el-button>
- </el-form-item>
- </el-form>
-
- <el-table
- :data="projects"
- style="width: 100%"
- v-loading="projectsLoading"
- empty-text="璇峰厛閫夋嫨姣旇禌"
- >
- <el-table-column prop="playerName" label="椤圭洰鍚嶇О" min-width="150">
- <template #default="scope">
- {{ scope.row.projectName || scope.row.playerName }}
- </template>
- </el-table-column>
- <el-table-column prop="playerName" label="鍙傝禌浜哄鍚�" min-width="120" />
- <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" min-width="120" />
- <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
- <template #default="scope">
- <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
- {{ scope.row.ratingCount }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center">
- <template #default="scope">
- <span v-if="scope.row.averageScore > 0" class="score">
- {{ scope.row.averageScore.toFixed(1) }}
- </span>
- <span v-else class="no-score">鏈瘎鍒�</span>
- </template>
- </el-table-column>
- <el-table-column prop="applyTime" label="鎶ュ悕鏃堕棿" width="180">
- <template #default="scope">
- {{ formatDate(scope.row.applyTime) }}
- </template>
- </el-table-column>
- <el-table-column prop="state" label="鐘舵��" width="100" align="center">
- <template #default="scope">
- <el-tag :type="getStateType(scope.row.state)">
- {{ getStateName(scope.row.state) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="鎿嶄綔" width="120" fixed="right">
- <template #default="scope">
- <el-button type="primary" link @click="viewDetails(scope.row.id)">
- 璇︽儏璇勫
- </el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <!-- 鍒嗛〉 -->
- <div class="pagination-container" v-if="total > 0">
- <el-pagination
- v-model:current-page="currentPage"
- v-model:page-size="pageSize"
- :page-sizes="[10, 20, 50, 100]"
- :total="total"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- />
+ <!-- 椤甸潰鏍囬鍖哄煙 -->
+ <div class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">椤圭洰璇勫</h1>
+ <p class="page-subtitle">绠$悊姣旇禌椤圭洰鐨勮瘎瀹℃祦绋嬶紝鏌ョ湅璇勫垎缁撴灉鍜岄」鐩鎯�</p>
</div>
- </el-card>
+
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
+ <div class="search-form">
+ <el-input
+ v-model="searchName"
+ placeholder="璇疯緭鍏ラ」鐩悕绉�"
+ @keyup.enter="loadProjects"
+ @clear="handleClear"
+ style="width: 180px"
+ clearable
+ />
+ <el-select
+ v-model="selectedActivity"
+ placeholder="璇烽�夋嫨姣旇禌"
+ @change="handleActivityChange"
+ style="width: 200px"
+ clearable
+ filterable
+ >
+ <template #prefix>
+ <el-icon><Trophy /></el-icon>
+ </template>
+ <el-option
+ v-for="activity in activities"
+ :key="activity.id"
+ :label="getActivityDisplayName(activity)"
+ :value="activity.id"
+ >
+ <span>{{ getActivityName(activity) }}</span>
+ <span v-if="activity.pid > 0" style="color: #409eff; margin-left: 8px;">
+ {{ activity.name }}
+ </span>
+ </el-option>
+ </el-select>
+ <el-button
+ type="primary"
+ :icon="Search"
+ @click="loadProjects"
+ :loading="projectsLoading"
+ >
+ 鏌ヨ
+ </el-button>
+ </div>
+ </div>
+
+ <el-table
+ :data="projects"
+ style="width: 100%"
+ v-loading="projectsLoading"
+ empty-text="璇峰厛閫夋嫨姣旇禌"
+ >
+ <el-table-column prop="playerName" label="椤圭洰鍚嶇О" min-width="150">
+ <template #default="scope">
+ {{ scope.row.projectName || scope.row.playerName }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="playerName" label="鍙傝禌浜哄鍚�" min-width="120" />
+ <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" min-width="120" />
+ <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
+ {{ scope.row.ratingCount }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center">
+ <template #default="scope">
+ <span v-if="scope.row.averageScore > 0" class="score">
+ {{ scope.row.averageScore.toFixed(1) }}
+ </span>
+ <span v-else class="no-score">鏈瘎鍒�</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="applyTime" label="鎶ュ悕鏃堕棿" width="180">
+ <template #default="scope">
+ {{ formatDate(scope.row.applyTime) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="reviewStatus" label="璇勫鐘舵��" width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="getReviewStatusType(scope.row.ratingCount)">
+ {{ getReviewStatusName(scope.row.ratingCount) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="80" align="center">
+ <template #default="scope">
+ <el-button
+ text
+ :icon="View"
+ @click="viewDetails(scope.row.id)"
+ class="action-btn view-btn"
+ title="璇︽儏璇勫"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <div class="pagination-container" v-if="total > 0">
+ <el-pagination
+ v-model:current-page="currentPage"
+ v-model:page-size="pageSize"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ />
+ </div>
</div>
</template>
@@ -117,7 +126,9 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
-import { getActiveActivities, getCompetitionProjects } from '@/api/projectReview'
+import { Search, Trophy, View } from '@element-plus/icons-vue'
+import { getActiveActivities, getRatingStats } from '@/api/projectReview'
+import { getProjectReviewApplications } from '@/api/projectReviewNew'
const router = useRouter()
@@ -136,14 +147,21 @@
// 鍔犺浇姣旇禌鍒楄〃
const loadActivities = async () => {
+ console.log('=== 寮�濮嬪姞杞芥瘮璧涘垪琛� ===')
activitiesLoading.value = true
try {
+ console.log('璋冪敤 getActiveActivities...')
const data = await getActiveActivities()
+ console.log('getActiveActivities 杩斿洖鏁版嵁:', data)
activities.value = data
+ console.log('activities.value 璁剧疆涓�:', activities.value)
+ console.log('activities.value.length:', activities.value?.length)
} catch (error) {
+ console.error('鍔犺浇姣旇禌鍒楄〃澶辫触:', error)
ElMessage.error(error.message)
} finally {
activitiesLoading.value = false
+ console.log('=== 姣旇禌鍒楄〃鍔犺浇瀹屾垚 ===')
}
}
@@ -154,17 +172,37 @@
return
}
+ console.log('=== 寮�濮嬪姞杞介」鐩垪琛� ===')
+ console.log('selectedActivity.value:', selectedActivity.value)
+ console.log('currentPage.value:', currentPage.value)
+ console.log('pageSize.value:', pageSize.value)
+
projectsLoading.value = true
try {
- const response = await getCompetitionProjects(
- selectedActivity.value,
- currentPage.value - 1, // 鍚庣浠�0寮�濮�
- pageSize.value,
- searchName.value
- )
+ const params = {
+ activityId: selectedActivity.value,
+ page: currentPage.value,
+ size: pageSize.value
+ }
- projects.value = response.content || []
- total.value = response.totalElements || 0
+ // 濡傛灉鏈夋悳绱㈠悕绉帮紝娣诲姞鍒板弬鏁颁腑
+ if (searchName.value && searchName.value.trim()) {
+ params.name = searchName.value.trim()
+ }
+
+ console.log('API璋冪敤鍙傛暟:', params)
+
+ const response = await getProjectReviewApplications(params)
+ console.log('API鍝嶅簲:', response)
+
+ // 澶勭悊鍝嶅簲鏁版嵁 - 鏂扮殑鍒嗛〉缁撴瀯
+ const pageData = response.projectReviewApplications
+ projects.value = pageData?.content || []
+ total.value = pageData?.totalElements || 0
+
+ console.log('璁剧疆 projects.value:', projects.value)
+ console.log('projects.value.length:', projects.value.length)
+ console.log('璁剧疆 total.value:', total.value)
} catch (error) {
console.error('鍔犺浇椤圭洰鍒楄〃澶辫触:', error)
ElMessage.error('鍔犺浇椤圭洰鍒楄〃澶辫触')
@@ -182,13 +220,22 @@
loadProjects()
}
-// 閲嶇疆鎼滅储
-const resetSearch = () => {
+// 娓呯┖鎼滅储
+const handleClear = () => {
searchName.value = ''
currentPage.value = 1
if (selectedActivity.value) {
loadProjects()
}
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+ searchName.value = ''
+ selectedActivity.value = null
+ currentPage.value = 1
+ projects.value = []
+ total.value = 0
}
// 鍒嗛〉澶勭悊
@@ -236,6 +283,16 @@
return stateMap[state] || '鏈煡'
}
+// 鑾峰彇璇勫鐘舵�佺被鍨嬶紙鍩轰簬璇勫娆℃暟锛�
+const getReviewStatusType = (ratingCount) => {
+ return ratingCount > 0 ? 'success' : 'warning'
+}
+
+// 鑾峰彇璇勫鐘舵�佸悕绉帮紙鍩轰簬璇勫娆℃暟锛�
+const getReviewStatusName = (ratingCount) => {
+ return ratingCount > 0 ? '宸茶瘎瀹�' : '鏈瘎瀹�'
+}
+
// 鑾峰彇姣旇禌鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥炵埗姣旇禌鍚嶇О锛涘鏋滄槸姣旇禌锛岃繑鍥炶嚜宸辩殑鍚嶇О锛�
const getActivityName = (activity) => {
if (activity.pid > 0 && activity.parent) {
@@ -257,6 +314,8 @@
return activity.name
}
+
+
// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
onMounted(() => {
loadActivities()
@@ -268,23 +327,63 @@
padding: 20px;
}
-.card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
+/* 椤甸潰鏍囬鍖哄煙 */
+.page-header {
+ margin-bottom: 24px;
}
-.card-title {
+.title-section {
+ text-align: left;
+}
+
+.page-title {
+ font-size: 24px;
+ font-weight: 600;
+ color: #1f2937;
+ margin: 0 0 8px 0;
+}
+
+.page-subtitle {
+ font-size: 14px;
+ color: #6b7280;
margin: 0;
- font-size: 18px;
- font-weight: 500;
+ line-height: 1.5;
+}
+
+/* 鎼滅储宸ュ叿鏍� */
+.search-toolbar {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 20px;
}
.search-form {
- margin-bottom: 20px;
- padding: 20px;
- background-color: #f5f7fa;
- border-radius: 4px;
+ display: flex;
+ gap: 12px;
+ align-items: center;
+}
+
+.search-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+/* 鎿嶄綔鎸夐挳鏍峰紡 */
+.action-btn {
+ padding: 8px !important;
+ margin: 0 6px;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.view-btn {
+ color: #3b82f6 !important;
+}
+
+.view-btn:hover {
+ background-color: rgba(59, 130, 246, 0.1) !important;
+ transform: scale(1.2);
}
.score {
@@ -314,4 +413,18 @@
:deep(.el-tag) {
border-radius: 12px;
}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .search-toolbar {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ }
+
+ .search-area {
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+}
</style>
\ No newline at end of file
diff --git a/web/src/views/rating/index.vue b/web/src/views/rating/index.vue
index b562d33..be6dc55 100644
--- a/web/src/views/rating/index.vue
+++ b/web/src/views/rating/index.vue
@@ -1,48 +1,71 @@
<template>
<div class="rating-page">
- <div class="page-card">
- <h3 class="card-title">璇勫垎妯℃澘绠$悊</h3>
-
- <!-- 鎿嶄綔鏍� -->
- <div class="toolbar">
- <el-button type="success" @click="handleAdd">
+ <!-- 椤甸潰鏍囬鍖哄煙 -->
+ <div class="page-header">
+ <div class="title-section">
+ <h1 class="page-title">璇勫垎妯℃澘</h1>
+ <p class="page-subtitle">绠$悊姣旇禌璇勫垎妯℃澘锛岃缃瘎鍒嗘爣鍑嗗拰鏉冮噸鍒嗛厤</p>
+ </div>
+ </div>
+
+ <!-- 鎼滅储宸ュ叿鏍� -->
+ <div class="search-toolbar">
+ <div class="search-form">
+ <el-input
+ v-model="searchQuery"
+ placeholder="鎼滅储璇勫垎妯℃澘..."
+ style="width: 300px"
+ clearable
+ @clear="handleClear"
+ />
+ <el-button type="primary" @click="handleSearch">
+ <el-icon><Search /></el-icon>
+ 鏌ヨ
+ </el-button>
+ <el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
鏂板妯℃澘
</el-button>
</div>
+ </div>
- <!-- 妯℃澘鍒楄〃 -->
- <el-table :data="tableData" style="width: 100%" v-loading="loading">
- <el-table-column prop="name" label="妯℃澘鍚嶇О" min-width="200" />
- <el-table-column prop="totalScore" label="妯℃澘鎬诲垎" width="120" align="center" />
- <el-table-column prop="itemCount" label="璇勫垎鏉$洰鏁�" width="120" align="center" />
- <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" />
- <el-table-column label="鎿嶄綔" width="180" fixed="right">
- <template #default="{ row }">
- <div class="table-actions">
- <el-button type="warning" size="small" @click="handleEdit(row)">
- 缂栬緫
- </el-button>
- <el-button type="danger" size="small" @click="handleDelete(row)">
- 鍒犻櫎
- </el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
+ <!-- 妯℃澘鍒楄〃 -->
+ <el-table :data="tableData" style="width: 100%" v-loading="loading">
+ <el-table-column prop="name" label="妯℃澘鍚嶇О" min-width="200" />
+ <el-table-column prop="totalScore" label="妯℃澘鎬诲垎" width="120" align="center" />
+ <el-table-column prop="itemCount" label="璇勫垎鏉$洰鏁�" width="120" align="center" />
+ <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" />
+ <el-table-column label="鎿嶄綔" width="120" align="center">
+ <template #default="{ row }">
+ <el-button
+ text
+ :icon="Edit"
+ @click="handleEdit(row)"
+ class="action-btn edit-btn"
+ title="缂栬緫"
+ />
+ <el-button
+ text
+ :icon="Delete"
+ @click="handleDelete(row)"
+ class="action-btn delete-btn"
+ title="鍒犻櫎"
+ />
+ </template>
+ </el-table-column>
+ </el-table>
- <!-- 鍒嗛〉 -->
- <div class="pagination">
- <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 class="pagination">
+ <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>
@@ -51,9 +74,11 @@
import { reactive, ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
+import { Plus, Search, Edit, Delete } from '@element-plus/icons-vue'
import { getRatingSchemes, deleteRatingScheme } from '@/api/rating.js'
const loading = ref(false)
+const searchQuery = ref('')
// 鍒嗛〉淇℃伅
const pagination = reactive({
@@ -96,6 +121,18 @@
}
}
+// 鎼滅储澶勭悊
+const handleSearch = () => {
+ pagination.page = 1
+ loadData()
+}
+
+const handleClear = () => {
+ searchQuery.value = ''
+ pagination.page = 1
+ loadData()
+}
+
// 鍒嗛〉澶у皬鏀瑰彉
const handleSizeChange = (size: number) => {
pagination.size = size
@@ -129,30 +166,100 @@
<style lang="scss" scoped>
.rating-page {
- .card-title {
- margin-bottom: 20px;
- color: #303133;
- font-size: 16px;
- font-weight: 500;
- }
-
- .toolbar {
- display: flex;
+ padding: 20px;
+}
+
+/* 椤甸潰鏍囬鍖哄煙 */
+.page-header {
+ margin-bottom: 24px;
+}
+
+.title-section {
+ text-align: left;
+}
+
+.page-title {
+ font-size: 24px;
+ font-weight: 600;
+ color: #1f2937;
+ margin: 0 0 8px 0;
+}
+
+.page-subtitle {
+ font-size: 14px;
+ color: #6b7280;
+ margin: 0;
+ line-height: 1.5;
+}
+
+/* 鎼滅储宸ュ叿鏍� */
+.search-toolbar {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 20px;
+ padding: 16px;
+ background-color: #f9fafb;
+ border-radius: 8px;
+ border: 1px solid #e5e7eb;
+}
+
+.search-form {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+/* 鎼滅储鎸夐挳 */
+.search-btn {
+ width: 24px !important;
+ height: 24px !important;
+ min-height: 24px !important;
+ padding: 0 !important;
+ margin-right: 8px;
+}
+
+/* 鎿嶄綔鎸夐挳鏍峰紡 */
+.action-btn {
+ padding: 8px !important;
+ margin: 0 6px;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.edit-btn {
+ color: #3b82f6 !important;
+}
+
+.edit-btn:hover {
+ background-color: rgba(59, 130, 246, 0.1) !important;
+ transform: scale(1.2);
+}
+
+.delete-btn {
+ color: #ef4444 !important;
+}
+
+.delete-btn:hover {
+ background-color: rgba(239, 68, 68, 0.1) !important;
+ transform: scale(1.2);
+}
+
+.pagination {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 768px) {
+ .search-toolbar {
+ flex-direction: column;
gap: 12px;
- margin-bottom: 20px;
- align-items: center;
+ align-items: stretch;
}
- .table-actions {
- display: flex;
- gap: 8px;
- flex-wrap: wrap;
- }
-
- .pagination {
- margin-top: 20px;
- display: flex;
- justify-content: flex-end;
+ .search-area {
+ justify-content: center;
}
}
</style>
\ No newline at end of file
diff --git a/web/src/views/review/index.vue b/web/src/views/review/index.vue
index ff306e3..2ddda57 100644
--- a/web/src/views/review/index.vue
+++ b/web/src/views/review/index.vue
@@ -38,7 +38,6 @@
<el-button type="primary" @click="loadProjects" :loading="projectsLoading">
鎼滅储
</el-button>
- <el-button @click="resetSearch">閲嶇疆</el-button>
</el-form-item>
</el-form>
@@ -130,14 +129,20 @@
// 鍔犺浇姣旇禌鍒楄〃
const loadActivities = async () => {
+ console.log('=== 寮�濮嬪姞杞芥瘮璧涘垪琛� ===')
activitiesLoading.value = true
try {
+ console.log('璋冪敤 getActiveActivities...')
const data = await getActiveActivities()
+ console.log('getActiveActivities 杩斿洖鏁版嵁:', data)
activities.value = data
+ console.log('activities.value 璁剧疆涓�:', activities.value)
} catch (error) {
+ console.error('鍔犺浇姣旇禌鍒楄〃澶辫触:', error)
ElMessage.error(error.message)
} finally {
activitiesLoading.value = false
+ console.log('=== 姣旇禌鍒楄〃鍔犺浇瀹屾垚 ===')
}
}
@@ -243,6 +248,8 @@
return activity.name
}
+
+
// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
onMounted(() => {
loadActivities()
diff --git a/web/ui_update.md b/web/ui_update.md
new file mode 100644
index 0000000..ed6282c
--- /dev/null
+++ b/web/ui_update.md
@@ -0,0 +1,342 @@
+# UI璁捐鏇存柊璁″垝
+
+## Dashboard椤甸潰瀹屾暣鏀归�犳柟妗�
+
+### 鎶�鏈爤纭
+- **鍥炬爣搴�**: @element-plus/icons-vue (宸插畨瑁�)
+- **瀛椾綋**: 绯荤粺榛樿瀛椾綋鏍�
+- **鏃ユ湡鏍煎紡**: 缁熶竴涓� "2025骞�10鏈�1鏃� 14:00" 鏍煎紡
+
+### 鏁翠綋椤甸潰璁捐
+
+#### 椤甸潰鑳屾櫙鍜屽鍣�
+- **椤甸潰鑳屾櫙鑹�**: #F8F9FA (娴呯伆鑹�)
+- **涓诲鍣�**: 淇濇寔褰撳墠鍝嶅簲寮忔爡鏍煎竷灞�
+- **瀹瑰櫒鍐呰竟璺�**: 24px
+- **鍗$墖闂磋窛**: 20px (淇濇寔鐜版湁)
+
+### 1. 缁熻鍗$墖鍖哄煙璇︾粏璁捐
+
+#### 1.1 鍗$墖瀹瑰櫒鏍峰紡
+```css
+.stat-card {
+ background: #FFFFFF;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+ border: none;
+ padding: 24px;
+ height: 120px;
+ position: relative;
+ overflow: hidden;
+}
+```
+
+#### 1.2 鍥涗釜缁熻鍗$墖鍏蜂綋璁捐
+
+**鍗$墖1 - 褰撳墠杩涜姣旇禌**
+- **鍥炬爣**: `<Calendar />` (Element Plus Icons)
+- **鍥炬爣瀹瑰櫒**:
+ - 灏哄: 48px 脳 48px
+ - 鑳屾櫙鑹�: #E0E7FF (娴呰摑鑹插渾褰�)
+ - 鍥炬爣棰滆壊: #6366F1 (钃濈传鑹�)
+ - 鍥炬爣灏哄: 24px
+ - 浣嶇疆: 宸︿笂瑙�
+- **鏁板瓧鏄剧ず**:
+ - 瀛椾綋澶у皬: 32px
+ - 瀛楅噸: 700 (bold)
+ - 棰滆壊: #1F2937 (娣辩伆)
+ - 浣嶇疆: 鍙充笂瑙掞紝鍙冲榻�
+- **鏍囬鏂囧瓧**:
+ - 鍐呭: "褰撳墠杩涜姣旇禌"
+ - 瀛椾綋澶у皬: 14px
+ - 瀛楅噸: 500 (medium)
+ - 棰滆壊: #6B7280 (涓伆)
+ - 浣嶇疆: 宸︿笅瑙�
+
+**鍗$墖2 - 鍙傝禌鎬讳汉鏁�**
+- **鍥炬爣**: `<User />` (Element Plus Icons)
+- **鍥炬爣瀹瑰櫒**:
+ - 鑳屾櫙鑹�: #D1FAE5 (娴呯豢鑹插渾褰�)
+ - 鍥炬爣棰滆壊: #10B981 (缁胯壊)
+- **鏁板瓧鍜屾枃瀛楁牱寮�**: 鍚屽崱鐗�1
+- **鏍囬**: "鍙傝禌鎬讳汉鏁�"
+
+**鍗$墖3 - 鎶ュ悕寰呭鏍�**
+- **鍥炬爣**: `<Document />` (Element Plus Icons)
+- **鍥炬爣瀹瑰櫒**:
+ - 鑳屾櫙鑹�: #FEF3C7 (娴呴粍鑹插渾褰�)
+ - 鍥炬爣棰滆壊: #F59E0B (姗欓粍鑹�)
+- **鏁板瓧鍜屾枃瀛楁牱寮�**: 鍚屽崱鐗�1
+- **鏍囬**: "鎶ュ悕寰呭鏍�"
+
+**鍗$墖4 - 璇勫鎬绘暟**
+- **鍥炬爣**: `<UserFilled />` (Element Plus Icons)
+- **鍥炬爣瀹瑰櫒**:
+ - 鑳屾櫙鑹�: #FECACA (娴呯孩鑹插渾褰�)
+ - 鍥炬爣棰滆壊: #EF4444 (绾㈣壊)
+- **鏁板瓧鍜屾枃瀛楁牱寮�**: 鍚屽崱鐗�1
+- **鏍囬**: "璇勫鎬绘暟"
+
+#### 1.3 鍗$墖鍐呴儴甯冨眬缁撴瀯
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� [馃棑锔廬 32 鈹�
+鈹� 鈹�
+鈹� 褰撳墠杩涜姣旇禌 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+### 2. 鏈�杩戞瘮璧涜〃鏍煎尯鍩熻璁�
+
+#### 2.1 琛ㄦ牸瀹瑰櫒鍗$墖
+```css
+.table-card {
+ background: #FFFFFF;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+ border: none;
+ padding: 24px;
+ margin-top: 20px;
+}
+```
+
+#### 2.2 琛ㄦ牸澶撮儴璁捐
+- **鏍囬**: "鏈�杩戞瘮璧�"
+- **鏍囬鏍峰紡**:
+ - 瀛椾綋澶у皬: 18px
+ - 瀛楅噸: 600 (semibold)
+ - 棰滆壊: #1F2937 (娣辩伆)
+- **鎸夐挳**: 绉婚櫎鎵�鏈夋寜閽� (鎸夌敤鎴疯姹�)
+
+#### 2.3 琛ㄦ牸鏍峰紡璇︾粏璁捐
+
+**琛ㄥご鏍峰紡**:
+```css
+.el-table__header {
+ background-color: #F9FAFB;
+}
+.el-table__header th {
+ background-color: #F9FAFB !important;
+ color: #374151;
+ font-size: 14px;
+ font-weight: 500;
+ height: 48px;
+ border-bottom: 1px solid #E5E7EB;
+}
+```
+
+**琛ㄦ牸琛屾牱寮�**:
+```css
+.el-table__row {
+ height: 56px;
+}
+.el-table__row:nth-child(even) {
+ background-color: #F9FAFB;
+}
+.el-table__row:nth-child(odd) {
+ background-color: #FFFFFF;
+}
+.el-table td {
+ color: #1F2937;
+ font-size: 14px;
+ font-weight: 400;
+ border-bottom: 1px solid #F3F4F6;
+}
+```
+
+#### 2.4 琛ㄦ牸鍒楄璁�
+1. **姣旇禌鍚嶇О** (180px)
+2. **鎶ュ悕浜烘暟** (120px)
+3. **姣旇禌鏃堕棿** (200px) - 鏍煎紡: "2025骞�10鏈�1鏃� 14:00"
+4. **鐘舵��** (100px) - 鐘舵�佹爣绛�
+5. **鎿嶄綔** (鑷�傚簲) - 绉婚櫎鎿嶄綔鎸夐挳
+
+#### 2.5 鐘舵�佹爣绛炬牱寮�
+
+**宸插彂甯冪姸鎬�**:
+```css
+.status-published {
+ background-color: #D1FAE5;
+ color: #065F46;
+ border-radius: 16px;
+ padding: 4px 12px;
+ font-size: 12px;
+ font-weight: 500;
+ display: inline-block;
+}
+```
+
+**鏈彂甯冪姸鎬�**:
+```css
+.status-unpublished {
+ background-color: #FEF3C7;
+ color: #92400E;
+ border-radius: 16px;
+ padding: 4px 12px;
+ font-size: 12px;
+ font-weight: 500;
+ display: inline-block;
+}
+```
+
+**鍏抽棴鐘舵��**:
+```css
+.status-closed {
+ background-color: #FEE2E2;
+ color: #991B1B;
+ border-radius: 16px;
+ padding: 4px 12px;
+ font-size: 12px;
+ font-weight: 500;
+ display: inline-block;
+}
+```
+
+### 3. 鍝嶅簲寮忚璁′繚鎸�
+
+#### 鏂偣璁剧疆 (淇濇寔鐜版湁)
+- **xl (鈮�1200px)**: 4鍒楃粺璁″崱鐗� (span=6)
+- **lg (鈮�992px)**: 4鍒楃粺璁″崱鐗� (span=6)
+- **md (鈮�768px)**: 2鍒楃粺璁″崱鐗� (span=12)
+- **sm (<768px)**: 1鍒楃粺璁″崱鐗� (span=24)
+
+#### 绉诲姩绔紭鍖�
+- 缁熻鍗$墖鍦ㄥ皬灞忓箷涓嬩繚鎸佺浉鍚岃璁�
+- 琛ㄦ牸鍦ㄧЩ鍔ㄧ鍙í鍚戞粴鍔�
+- 鍥炬爣鍜屾枃瀛楁瘮渚嬩繚鎸佷竴鑷�
+
+### 4. 鏁版嵁鏍煎紡澶勭悊
+
+#### 鏃ユ湡鏍煎紡杞崲
+```javascript
+// 灏嗗悗绔繑鍥炵殑鏃ユ湡鏍煎紡鍖栦负: "2025骞�10鏈�1鏃� 14:00"
+const formatDate = (dateString) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString('zh-CN', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+}
+```
+
+#### 鐘舵�佹槧灏�
+```javascript
+const statusMap = {
+ '宸插彂甯�': 'published',
+ '鏈彂甯�': 'unpublished',
+ '鍏抽棴': 'closed'
+}
+```
+
+### 5. 閬楁紡鐨勮璁$粏鑺傝ˉ鍏�
+
+#### 5.1 鍗$墖hover鏁堟灉
+```css
+.stat-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+ transition: all 0.3s ease;
+}
+```
+
+#### 5.2 鍥炬爣瀹瑰櫒鍦嗗舰璁捐
+```css
+.icon-container {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 24px;
+ left: 24px;
+}
+```
+
+#### 5.3 鏁板瓧鍔ㄧ敾鏁堟灉 (鍙��)
+- 椤甸潰鍔犺浇鏃舵暟瀛椾粠0寮�濮嬭鏁板埌鐩爣鍊�
+- 浣跨敤CSS transition瀹炵幇骞虫粦杩囨浮
+
+#### 5.4 琛ㄦ牸鏃犳暟鎹姸鎬�
+```css
+.el-table__empty-block {
+ background-color: #F9FAFB;
+ color: #6B7280;
+ font-size: 14px;
+}
+```
+
+#### 5.5 鍔犺浇鐘舵�佽璁�
+- 缁熻鍗$墖鏄剧ず楠ㄦ灦灞�
+- 琛ㄦ牸鏄剧ずloading鐘舵��
+- 淇濇寔涓庢暣浣撹璁¢鏍间竴鑷�
+
+### 6. 瀹炴柦浼樺厛绾�
+
+#### 绗竴闃舵 (鏍稿績鏍峰紡)
+1. 椤甸潰鑳屾櫙鑹蹭慨鏀�
+2. 缁熻鍗$墖閲嶆瀯 (鍥炬爣 + 鏍峰紡)
+3. 琛ㄦ牸鍩虹鏍峰紡鏇存柊
+
+#### 绗簩闃舵 (缁嗚妭浼樺寲)
+1. 鐘舵�佹爣绛炬牱寮�
+2. 鏃ユ湡鏍煎紡澶勭悊
+3. 鍝嶅簲寮忔祴璇�
+
+#### 绗笁闃舵 (浜や簰浼樺寲)
+1. hover鏁堟灉
+2. 鍔犺浇鐘舵��
+3. 鍔ㄧ敾鏁堟灉
+
+### 7. 闇�瑕佸垱寤虹殑鏂扮粍浠�
+
+#### StatCard.vue (缁熻鍗$墖缁勪欢)
+- 鎺ユ敹鍥炬爣銆侀鑹层�佹暟鍊笺�佹爣棰樼瓑props
+- 鍙鐢ㄧ殑缁熻鍗$墖缁勪欢
+- 鍐呯疆hover鍜屽姩鐢绘晥鏋�
+
+#### StatusTag.vue (鐘舵�佹爣绛剧粍浠�)
+- 鏍规嵁鐘舵�佸�艰嚜鍔ㄥ簲鐢ㄥ搴旀牱寮�
+- 鏀寔鑷畾涔夐鑹叉槧灏�
+
+### 8. CSS鍙橀噺瀹氫箟
+
+```css
+:root {
+ /* 涓婚鑹� */
+ --primary-color: #6366F1;
+ --bg-color: #F8F9FA;
+ --card-bg: #FFFFFF;
+
+ /* 缁熻鍗$墖棰滆壊 */
+ --blue-light: #E0E7FF;
+ --blue-primary: #6366F1;
+ --green-light: #D1FAE5;
+ --green-primary: #10B981;
+ --yellow-light: #FEF3C7;
+ --yellow-primary: #F59E0B;
+ --red-light: #FECACA;
+ --red-primary: #EF4444;
+
+ /* 鏂囧瓧棰滆壊 */
+ --text-primary: #1F2937;
+ --text-secondary: #6B7280;
+ --text-muted: #374151;
+
+ /* 鐘舵�侀鑹� */
+ --status-published-bg: #D1FAE5;
+ --status-published-text: #065F46;
+ --status-unpublished-bg: #FEF3C7;
+ --status-unpublished-text: #92400E;
+ --status-closed-bg: #FEE2E2;
+ --status-closed-text: #991B1B;
+}
+```
+
+---
+
+*姝よ鍒掓兜鐩栦簡鍥剧墖璁捐鐨勬墍鏈夎瑙夊厓绱狅紝纭繚楂樺害杩樺師鐩爣鏁堟灉*
\ No newline at end of file
diff --git a/web/web_update.md b/web/web_update.md
new file mode 100644
index 0000000..64ce2fa
--- /dev/null
+++ b/web/web_update.md
@@ -0,0 +1,179 @@
+# Web鍓嶇鏇存柊璁″垝
+
+## Dashboard椤甸潰鏀归�犺鍒�
+
+### 鏁翠綋椤甸潰甯冨眬
+**褰撳墠鐘舵�佸垎鏋愶細**
+- 椤甸潰鑳屾櫙锛氶粯璁ょ櫧鑹�
+- 鏁翠綋甯冨眬锛氫娇鐢‥lement Plus鐨勬爡鏍肩郴缁�
+- 闂磋窛锛氱粺涓�浣跨敤20px闂磋窛
+
+**鐩爣璁捐瑕佹眰锛�**
+- 椤甸潰鑳屾櫙鑹诧細#F8F9FA锛堟祬鐏拌壊锛�
+- 淇濇寔鍝嶅簲寮忔爡鏍煎竷灞�
+- 鍗$墖闂磋窛淇濇寔20px
+
+### 1. 缁熻鍗$墖鍖哄煙鏀归��
+
+#### 1.1 鍗$墖鏁翠綋鏍峰紡
+**褰撳墠鏍峰紡锛�**
+```css
+.box-card {
+ width: 100%;
+}
+```
+
+**鐩爣鏍峰紡瑙勮寖锛�**
+- 鑳屾櫙鑹诧細#FFFFFF锛堢函鐧斤級
+- 鍦嗚锛�12px
+- 闃村奖锛�0 4px 12px rgba(0, 0, 0, 0.08)
+- 杈规锛氭棤
+- 鍐呰竟璺濓細24px
+
+#### 1.2 缁熻鍗$墖鍏蜂綋璁捐
+
+**鍗$墖1 - 褰撳墠杩涜姣旇禌**
+- 鍥炬爣锛氭棩鍘嗗浘鏍囷紙Calendar锛�
+- 鍥炬爣鑳屾櫙锛�#E0E7FF锛堟祬钃濊壊鍦嗗舰鑳屾櫙锛�
+- 鍥炬爣棰滆壊锛�#6366F1锛堣摑绱壊锛�
+- 鏁板瓧棰滆壊锛�#1F2937锛堟繁鐏拌壊锛�
+- 鏁板瓧瀛椾綋锛�32px锛宖ont-weight: 700
+- 鏍囬棰滆壊锛�#6B7280锛堜腑鐏拌壊锛�
+- 鏍囬瀛椾綋锛�14px锛宖ont-weight: 500
+
+**鍗$墖2 - 鍙傝禌鎬讳汉鏁�**
+- 鍥炬爣锛氱敤鎴风兢缁勫浘鏍囷紙Users锛�
+- 鍥炬爣鑳屾櫙锛�#D1FAE5锛堟祬缁胯壊鍦嗗舰鑳屾櫙锛�
+- 鍥炬爣棰滆壊锛�#10B981锛堢豢鑹诧級
+- 鏁板瓧鍜屾爣棰樻牱寮忓悓鍗$墖1
+
+**鍗$墖3 - 鎶ュ悕寰呭鏍�**
+- 鍥炬爣锛氳鍛婂浘鏍囷紙Warning锛�
+- 鍥炬爣鑳屾櫙锛�#FEF3C7锛堟祬榛勮壊鍦嗗舰鑳屾櫙锛�
+- 鍥炬爣棰滆壊锛�#F59E0B锛堟榛勮壊锛�
+- 鏁板瓧鍜屾爣棰樻牱寮忓悓鍗$墖1
+
+**鍗$墖4 - 璇勫鎬绘暟**
+- 鍥炬爣锛氱敤鎴峰浘鏍囷紙User锛�
+- 鍥炬爣鑳屾櫙锛�#FECACA锛堟祬绾㈣壊鍦嗗舰鑳屾櫙锛�
+- 鍥炬爣棰滆壊锛�#EF4444锛堢孩鑹诧級
+- 鏁板瓧鍜屾爣棰樻牱寮忓悓鍗$墖1
+
+#### 1.3 鍗$墖鍐呴儴甯冨眬
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� [鍥炬爣] [鏁板瓧] 鈹�
+鈹� 鏍囬鏂囧瓧 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+- 鍥炬爣灏哄锛�48px 脳 48px鍦嗗舰鑳屾櫙锛屽唴閮ㄥ浘鏍�24px
+- 鍥炬爣浣嶇疆锛氬乏涓婅
+- 鏁板瓧浣嶇疆锛氬彸涓婅锛屽彸瀵归綈
+- 鏍囬浣嶇疆锛氬乏涓嬭
+
+### 2. 鏈�杩戞瘮璧涜〃鏍煎尯鍩熸敼閫�
+
+#### 2.1 琛ㄦ牸瀹瑰櫒鍗$墖
+**鐩爣鏍峰紡锛�**
+- 鑳屾櫙鑹诧細#FFFFFF
+- 鍦嗚锛�12px
+- 闃村奖锛�0 4px 12px rgba(0, 0, 0, 0.08)
+- 鍐呰竟璺濓細24px
+
+#### 2.2 琛ㄦ牸澶撮儴
+**褰撳墠锛�**
+```html
+<div class="card-header">
+ <span>鏈�杩戞瘮璧�</span>
+ <el-button type="primary">鏌ョ湅鍏ㄩ儴</el-button>
+</div>
+```
+
+**鐩爣鏍峰紡锛�**
+- 鏍囬锛�"鏈�杩戞瘮璧�"
+- 鏍囬瀛椾綋锛�18px锛宖ont-weight: 600锛岄鑹诧細#1F2937
+- 鎸夐挳锛�"鍒涘缓鏂版瘮璧�"
+- 鎸夐挳鑳屾櫙锛�#6366F1锛堢传鑹诧級
+- 鎸夐挳鏂囧瓧锛�#FFFFFF
+- 鎸夐挳鍦嗚锛�8px
+- 鎸夐挳鍐呰竟璺濓細12px 20px
+
+#### 2.3 琛ㄦ牸鏍峰紡
+**琛ㄥご鏍峰紡锛�**
+- 鑳屾櫙鑹诧細#F9FAFB
+- 鏂囧瓧棰滆壊锛�#374151
+- 瀛椾綋锛�14px锛宖ont-weight: 500
+- 楂樺害锛�48px
+
+**琛ㄦ牸琛屾牱寮忥細**
+- 濂囨暟琛岃儗鏅細#FFFFFF
+- 鍋舵暟琛岃儗鏅細#F9FAFB
+- 琛岄珮锛�56px
+- 鏂囧瓧棰滆壊锛�#1F2937
+- 瀛椾綋锛�14px锛宖ont-weight: 400
+
+#### 2.4 鐘舵�佹爣绛炬牱寮�
+**宸插彂甯冪姸鎬侊細**
+- 鑳屾櫙鑹诧細#D1FAE5
+- 鏂囧瓧棰滆壊锛�#065F46
+- 鍦嗚锛�16px
+- 鍐呰竟璺濓細4px 12px
+- 瀛椾綋锛�12px锛宖ont-weight: 500
+
+**鏈彂甯冪姸鎬侊細**
+- 鑳屾櫙鑹诧細#FEF3C7
+- 鏂囧瓧棰滆壊锛�#92400E
+- 鍏朵粬鏍峰紡鍚屼笂
+
+#### 2.5 鎿嶄綔鎸夐挳鏍峰紡
+**鏌ョ湅鎸夐挳锛�**
+- 鑳屾櫙鑹诧細閫忔槑
+- 鏂囧瓧棰滆壊锛�#6366F1
+- 杈规锛�1px solid #E5E7EB
+- 鍦嗚锛�6px
+- 鍐呰竟璺濓細6px 12px
+
+**绠$悊鎸夐挳锛�**
+- 鑳屾櫙鑹诧細#6366F1
+- 鏂囧瓧棰滆壊锛�#FFFFFF
+- 鍦嗚锛�6px
+- 鍐呰竟璺濓細6px 12px
+
+### 3. 宸︿晶瀵艰埅鏍忥紙濡傞渶淇敼锛�
+**鐩爣鏍峰紡锛�**
+- 鑳屾櫙鑹诧細#1F2937锛堟繁鐏拌壊锛�
+- 閫変腑椤硅儗鏅細#6366F1
+- 鏂囧瓧棰滆壊锛�#D1D5DB
+- 閫変腑椤规枃瀛楋細#FFFFFF
+
+### 4. 椤堕儴瀵艰埅鏍�
+**鐩爣鏍峰紡锛�**
+- 鑳屾櫙鑹诧細#FFFFFF
+- 闃村奖锛�0 1px 3px rgba(0, 0, 0, 0.1)
+- 楂樺害锛�64px
+
+### 5. 鍝嶅簲寮忔柇鐐瑰鐞�
+**淇濇寔褰撳墠鍝嶅簲寮忛�昏緫锛�**
+- xl: 6鍒楀竷灞�锛堚墺1200px锛�
+- lg: 6鍒楀竷灞�锛堚墺992px锛�
+- md: 12鍒楀竷灞�锛堚墺768px锛�
+- sm: 24鍒楀竷灞�锛�<768px锛�
+
+### 6. 闇�瑕佺‘璁ょ殑缁嗚妭闂
+
+1. **鍥炬爣搴撻�夋嫨**锛氫娇鐢‥lement Plus Icons杩樻槸寮曞叆鍏朵粬鍥炬爣搴擄紵
+2. **瀛椾綋璁剧疆**锛氭槸鍚﹂渶瑕佸紩鍏ョ壒瀹氬瓧浣擄紝杩樻槸浣跨敤绯荤粺榛樿瀛椾綋锛�
+3. **琛ㄦ牸鏁版嵁鏍煎紡**锛氭瘮璧涙椂闂存樉绀烘牸寮忔槸鍚﹂渶瑕佽皟鏁达紵
+4. **鐘舵�佹槧灏�**锛氬綋鍓嶅悗绔繑鍥炵殑鐘舵�佸�间笌棰滆壊鐨勫搴斿叧绯伙紵
+
+### 7. 瀹炴柦姝ラ
+1. 鏇存柊鍏ㄥ眬CSS鍙橀噺瀹氫箟涓婚鑹�
+2. 淇敼椤甸潰鑳屾櫙鍜屽竷灞�瀹瑰櫒鏍峰紡
+3. 閲嶆瀯缁熻鍗$墖缁勪欢锛屾坊鍔犲浘鏍囧拰鏂版牱寮�
+4. 鏇存柊琛ㄦ牸鏍峰紡鍜岀姸鎬佹爣绛�
+5. 璋冩暣鎸夐挳鏍峰紡
+6. 娴嬭瘯鍝嶅簲寮忔晥鏋�
+
+---
+
+*娉細浠ヤ笂棰滆壊鍊煎熀浜庡浘鐗囧垎鏋愬緱鍑猴紝濡傛湁鍋忓樊璇峰強鏃跺弽棣堣皟鏁�*
\ No newline at end of file
diff --git a/wx/pages/registration/registration.js b/wx/pages/registration/registration.js
index f4ca562..0c62a99 100644
--- a/wx/pages/registration/registration.js
+++ b/wx/pages/registration/registration.js
@@ -111,7 +111,7 @@
// 鍔犺浇鍖哄煙鏁版嵁
this.loadRegions()
- }
+ },
// 鍔犺浇娲诲姩淇℃伅
async loadActivityInfo() {
diff --git "a/\347\241\254\347\274\226\347\240\201\351\227\256\351\242\230\345\210\206\346\236\220\346\212\245\345\221\212.md" "b/\347\241\254\347\274\226\347\240\201\351\227\256\351\242\230\345\210\206\346\236\220\346\212\245\345\221\212.md"
new file mode 100644
index 0000000..8ac91ac
--- /dev/null
+++ "b/\347\241\254\347\274\226\347\240\201\351\227\256\351\242\230\345\210\206\346\236\220\346\212\245\345\221\212.md"
@@ -0,0 +1,156 @@
+# 纭紪鐮侀棶棰樺垎鏋愭姤鍛�
+
+## 闂姒傝堪
+
+缁忚繃鍏ㄩ潰妫�鏌ワ紝鍙戠幇椤圭洰涓瓨鍦ㄥ澶勭‖缂栫爜闂锛屼富瑕侀泦涓湪姣旇禌闃舵鍚嶇О鐨勫鐞嗕笂銆傝繖浜涚‖缂栫爜浼氬鑷寸郴缁熷湪鐢ㄦ埛鑷畾涔夐樁娈靛悕绉版椂鍑虹幇鍔熻兘寮傚父銆�
+
+## 鍙戠幇鐨勭‖缂栫爜闂
+
+### 1. 鍚庣鏈嶅姟灞傜‖缂栫爜锛堜弗閲嶏級
+
+**鏂囦欢锛�** `backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java`
+**浣嶇疆锛�** 绗�35琛�
+**闂浠g爜锛�**
+```java
+// 榛樿鍙樉绀烘捣閫夐樁娈电殑鏁版嵁锛岃繃婊ゆ帀澶嶈禌绛夊悗缁樁娈�
+whereClause.append("(stage.name NOT LIKE '%澶嶈禌%' AND stage.name NOT LIKE '%鍐宠禌%')");
+```
+
+**闂鍒嗘瀽锛�**
+- 纭紪鐮佷簡"澶嶈禌"鍜�"鍐宠禌"瀛楃涓�
+- 濡傛灉鐢ㄦ埛鑷畾涔夐樁娈靛悕绉帮紙濡�"绗簩杞�"銆�"鍗婂喅璧�"绛夛級锛岃繃婊ら�昏緫灏嗗け鏁�
+- 杩欐槸鏈�涓ラ噸鐨勭‖缂栫爜闂锛岀洿鎺ュ奖鍝嶄笟鍔¢�昏緫
+
+### 2. 鍓嶇鐣岄潰纭紪鐮侊紙涓瓑锛�
+
+**鏂囦欢锛�** `web/src/views/ActivityForm.vue`
+**浣嶇疆锛�** 绗�668琛�
+**闂浠g爜锛�**
+```javascript
+const stageNames = ['', '娴烽��', '澶嶈禌', '鍗婂喅璧�', '鍐宠禌', '鎬诲喅璧�']
+```
+
+**闂鍒嗘瀽锛�**
+- 纭紪鐮佷簡榛樿闃舵鍚嶇О鏁扮粍
+- 闄愬埗浜嗙敤鎴风殑鑷畾涔夎兘鍔�
+- 涓嶅鐏垫椿锛屾棤娉曢�傚簲涓嶅悓绫诲瀷鐨勬瘮璧�
+
+### 3. 鍓嶇姣旇禌鏅嬬骇椤甸潰纭紪鐮侊紙涓瓑锛�
+
+**鏂囦欢锛�** `web/src/views/competition-promotion/index.vue`
+**浣嶇疆锛�** 绗�300琛屻�佺310琛屻�佺430琛�
+**闂浠g爜锛�**
+```javascript
+stageName: '鍒濊禌',
+stageName: '鍐宠禌',
+currentStageName: '鍒濊禌'
+```
+
+**闂鍒嗘瀽锛�**
+- 妯℃嫙鏁版嵁涓‖缂栫爜浜嗛樁娈靛悕绉�
+- 鍙兘褰卞搷娴嬭瘯鍜屽紑鍙戠幆澧冪殑鏁版嵁涓�鑷存��
+
+### 4. 鏁版嵁搴撴祴璇曟暟鎹‖缂栫爜锛堣交寰級
+
+**鏂囦欢锛�** `db.sql`
+**浣嶇疆锛�** 绗�39-45琛�
+**闂浠g爜锛�**
+```sql
+INSERT INTO `t_activity` (..., `name`, ...) VALUES (..., '娴烽��', ...);
+INSERT INTO `t_activity` (..., `name`, ...) VALUES (..., '澶嶈禌', ...);
+```
+
+**闂鍒嗘瀽锛�**
+- 鍒濆鍖栨暟鎹腑纭紪鐮佷簡闃舵鍚嶇О
+- 铏界劧鏄祴璇曟暟鎹紝浣嗗彲鑳借瀵煎紑鍙戣�呰涓鸿繖鏄爣鍑嗗懡鍚�
+
+## 鏍规湰鍘熷洜鍒嗘瀽
+
+### 1. 缂轰箯闃舵绫诲瀷璁捐
+- 鏁版嵁搴撹〃 `t_activity` 涓彧鏈� `sort_order` 瀛楁琛ㄧず闃舵椤哄簭
+- 缂哄皯 `stage_type` 鎴� `stage_level` 瀛楁鏉ユ爣璇嗛樁娈电被鍨�
+- 涓氬姟閫昏緫渚濊禆闃舵鍚嶇О鑰岄潪闃舵绫诲瀷
+
+### 2. 杩囨护閫昏緫璁捐涓嶅綋
+- 浣跨敤瀛楃涓插尮閰嶈�岄潪缁撴瀯鍖栧瓧娈佃繘琛岃繃婊�
+- 娌℃湁鑰冭檻鐢ㄦ埛鑷畾涔夐樁娈靛悕绉扮殑鍦烘櫙
+
+## 淇敼寤鸿
+
+### 鏂规涓�锛氭坊鍔犻樁娈电被鍨嬪瓧娈碉紙鎺ㄨ崘锛�
+
+1. **鏁版嵁搴撶粨鏋勪慨鏀�**
+```sql
+ALTER TABLE t_activity ADD COLUMN stage_type INT DEFAULT 1 COMMENT '闃舵绫诲瀷锛�1=鍒濋�夐樁娈碉紝2=涓骇闃舵锛�3=楂樼骇闃舵';
+```
+
+2. **鍚庣鏈嶅姟淇敼**
+```java
+// 鏇挎崲纭紪鐮佺殑瀛楃涓插尮閰�
+// 鍘熶唬鐮侊細whereClause.append("(stage.name NOT LIKE '%澶嶈禌%' AND stage.name NOT LIKE '%鍐宠禌%')");
+// 鏂颁唬鐮侊細
+whereClause.append("stage.stage_type = 1"); // 鍙樉绀哄垵閫夐樁娈�
+```
+
+3. **鍓嶇鐣岄潰淇敼**
+- 鍦ㄦ椿鍔ㄥ垱寤鸿〃鍗曚腑娣诲姞闃舵绫诲瀷閫夋嫨
+- 绉婚櫎纭紪鐮佺殑闃舵鍚嶇О鏁扮粍
+- 鍏佽鐢ㄦ埛鑷畾涔夐樁娈靛悕绉�
+
+### 鏂规浜岋細鍩轰簬鎺掑簭椤哄簭杩囨护锛堝閫夛級
+
+1. **鍚庣鏈嶅姟淇敼**
+```java
+// 鍩轰簬sort_order杩囨护锛屽彧鏄剧ず绗竴闃舵
+whereClause.append("stage.sort_order = 1");
+```
+
+2. **浼樼偣锛�** 鏃犻渶淇敼鏁版嵁搴撶粨鏋�
+3. **缂虹偣锛�** 鐏垫椿鎬ц緝宸紝鏃犳硶鏀寔澶嶆潅鐨勯樁娈靛垎绫�
+
+### 鏂规涓夛細閰嶇疆鍖栭樁娈电鐞嗭紙鏈�浣筹級
+
+1. **鍒涘缓闃舵绫诲瀷閰嶇疆琛�**
+```sql
+CREATE TABLE t_stage_type (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(50) NOT NULL COMMENT '闃舵绫诲瀷鍚嶇О',
+ code VARCHAR(20) NOT NULL COMMENT '闃舵绫诲瀷浠g爜',
+ level INT NOT NULL COMMENT '闃舵绾у埆',
+ is_initial BOOLEAN DEFAULT FALSE COMMENT '鏄惁涓哄垵閫夐樁娈�',
+ description VARCHAR(255) COMMENT '鎻忚堪'
+);
+```
+
+2. **淇敼娲诲姩琛�**
+```sql
+ALTER TABLE t_activity ADD COLUMN stage_type_id BIGINT COMMENT '闃舵绫诲瀷ID';
+```
+
+3. **涓氬姟閫昏緫淇敼**
+```java
+// 鍩轰簬闃舵绫诲瀷閰嶇疆杩涜杩囨护
+whereClause.append("EXISTS (SELECT 1 FROM t_stage_type st WHERE st.id = stage.stage_type_id AND st.is_initial = true)");
+```
+
+## 瀹炴柦浼樺厛绾�
+
+1. **楂樹紭鍏堢骇锛�** 淇 `PlayerApplicationService.java` 涓殑纭紪鐮佽繃婊ら�昏緫
+2. **涓紭鍏堢骇锛�** 淇敼鍓嶇闃舵鍚嶇О纭紪鐮�
+3. **浣庝紭鍏堢骇锛�** 娓呯悊娴嬭瘯鏁版嵁涓殑纭紪鐮�
+
+## 椋庨櫓璇勪及
+
+- **楂橀闄╋細** 鍚庣杩囨护閫昏緫淇敼鍙兘褰卞搷鐜版湁鏁版嵁鏌ヨ
+- **涓闄╋細** 鏁版嵁搴撶粨鏋勪慨鏀归渶瑕佹暟鎹縼绉�
+- **浣庨闄╋細** 鍓嶇鐣岄潰淇敼鐩稿瀹夊叏
+
+## 寤鸿瀹炴柦姝ラ
+
+1. 棣栧厛瀹炴柦鏂规浜岋紙鍩轰簬鎺掑簭椤哄簭锛夛紝蹇�熻В鍐冲綋鍓嶉棶棰�
+2. 鐒跺悗閫愭瀹炴柦鏂规涓夛紙閰嶇疆鍖栫鐞嗭級锛屾彁渚涢暱鏈熻В鍐虫柟妗�
+3. 鏈�鍚庢竻鐞嗘墍鏈夌浉鍏崇殑纭紪鐮侀棶棰�
+
+## 鎬荤粨
+
+椤圭洰涓殑纭紪鐮侀棶棰樹富瑕佹簮浜庣己涔忓悎鐞嗙殑闃舵绫诲瀷璁捐銆傚缓璁噰鐢ㄩ厤缃寲鐨勬柟寮忕鐞嗛樁娈电被鍨嬶紝鏃㈣兘瑙e喅褰撳墠鐨勭‖缂栫爜闂锛屽張鑳戒负鏈潵鐨勫姛鑳芥墿灞曟彁渚涜壇濂界殑鍩虹銆�
\ No newline at end of file
--
Gitblit v1.8.0