From cfdbaf1095cdad96e652b1a55ed0b774aa3e1f16 Mon Sep 17 00:00:00 2001 From: congsh Date: Sat, 18 Apr 2026 12:24:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20UI=20redesign=20v0.0.2=20=E2=80=94=20co?= =?UTF-8?q?lor=20unification,=20navigation=20improvements,=20mobile=20supp?= =?UTF-8?q?ort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Unify color palette from mixed green/blue/purple to consistent green theme - Sidebar: add text labels to create/join group buttons for discoverability - Header: add quick action buttons (create group, join group, notifications) - Mobile: add hamburger menu with slide-out sidebar and overlay - Home: add prominent CTA buttons, onboarding card for empty state - Join group dialog: add search-by-name mode alongside existing ID lookup - Games library: inline group selector dropdown instead of external selection Co-Authored-By: Claude Opus 4.7 --- frontend/src/api/groups.ts | 14 + frontend/src/assets/design.css | 8 +- .../src/components/game/GameDetailDialog.vue | 2 +- .../src/components/group/JoinGroupDialog.vue | 292 ++++++++++++-- .../src/components/team/IdleMembersList.vue | 2 +- .../src/components/team/InvitationCard.vue | 4 +- .../src/components/team/TeamSessionPanel.vue | 2 +- frontend/src/views/GamesLibrary.vue | 364 ++++++++++++++---- frontend/src/views/GroupView.vue | 2 +- frontend/src/views/Home.vue | 343 ++++++++++++----- frontend/src/views/Layout.vue | 259 +++++++++++-- frontend/src/views/Profile.vue | 2 +- frontend/src/views/Settings.vue | 4 +- 13 files changed, 1061 insertions(+), 237 deletions(-) diff --git a/frontend/src/api/groups.ts b/frontend/src/api/groups.ts index bfbb8fe..18ab9c7 100644 --- a/frontend/src/api/groups.ts +++ b/frontend/src/api/groups.ts @@ -38,6 +38,20 @@ export async function getGroup(groupId: string): Promise { }) as unknown as Group } +// 按名称搜索群组 +export async function searchGroups(keyword: string): Promise { + if (!keyword.trim()) return [] + + const user = pb.authStore.model + const filter = `name ~ "${keyword.trim()}" && id != "${user?.id}"` + + const result = await pb.collection('groups').getList(1, 20, { + filter, + $autoCancel: false + }) + return result.items as unknown as Group[] +} + // 直接加入群组(无需审核时调用) export async function joinGroup(groupId: string) { const user = pb.authStore.model diff --git a/frontend/src/assets/design.css b/frontend/src/assets/design.css index bada104..2d71f1f 100644 --- a/frontend/src/assets/design.css +++ b/frontend/src/assets/design.css @@ -4,9 +4,9 @@ --gg-primary-light: #10b981; --gg-primary-dark: #047857; - /* 辅助色:深紫 */ - --gg-accent: #7c3aed; - --gg-accent-light: #8b5cf6; + /* 辅助色:翠绿 */ + --gg-accent: #0d9488; + --gg-accent-light: #14b8a6; /* 背景色(亮色) */ --gg-bg: #f0fdf4; @@ -40,7 +40,7 @@ --gg-shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.1); /* 渐变 */ - --gg-gradient: linear-gradient(135deg, #059669 0%, #7c3aed 100%); + --gg-gradient: linear-gradient(135deg, #059669 0%, #0d9488 100%); --gg-gradient-green: linear-gradient(135deg, #10b981 0%, #059669 100%); --gg-gradient-danger: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); --gg-gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%); diff --git a/frontend/src/components/game/GameDetailDialog.vue b/frontend/src/components/game/GameDetailDialog.vue index cc59754..66bfff6 100644 --- a/frontend/src/components/game/GameDetailDialog.vue +++ b/frontend/src/components/game/GameDetailDialog.vue @@ -103,7 +103,7 @@ function handleCreateTeam() { .detail-name { margin: 0; font-size: 22px; font-weight: 700; color: var(--gg-text); text-align: center; } .detail-meta { display: flex; align-items: center; gap: 12px; } -.platform-badge { padding: 4px 14px; background: rgba(168, 85, 247, 0.15); color: var(--gg-accent); border-radius: 6px; font-size: 13px; font-weight: 600; } +.platform-badge { padding: 4px 14px; background: rgba(5, 150, 105, 0.15); color: var(--gg-accent); border-radius: 6px; font-size: 13px; font-weight: 600; } .popularity { font-size: 14px; color: var(--gg-text-secondary); } .detail-tags { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; } diff --git a/frontend/src/components/group/JoinGroupDialog.vue b/frontend/src/components/group/JoinGroupDialog.vue index 1a50e27..6b04a86 100644 --- a/frontend/src/components/group/JoinGroupDialog.vue +++ b/frontend/src/components/group/JoinGroupDialog.vue @@ -1,8 +1,10 @@ @@ -98,7 +218,39 @@ function reset() { .join-form { display: flex; flex-direction: column; - gap: 20px; + gap: 16px; +} + +/* ── 模式切换 ── */ +.mode-tabs { + display: flex; + gap: 4px; + padding: 4px; + background: var(--gg-bg); + border-radius: var(--gg-radius-sm); +} + +.mode-tab { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 9px 12px; + border: none; + border-radius: 6px; + background: transparent; + color: var(--gg-text-secondary); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.mode-tab--active { + background: var(--gg-bg-card); + color: var(--gg-primary); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); } .form-field { @@ -121,6 +273,54 @@ function reset() { flex: 1; } +/* ── 搜索结果列表 ── */ +.results-list { + display: flex; + flex-direction: column; + gap: 8px; + max-height: 280px; + overflow-y: auto; +} + +.result-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 14px; + background: var(--gg-bg); + border: 1px solid var(--gg-border); + border-radius: var(--gg-radius-sm); + cursor: pointer; + transition: all 0.2s; +} + +.result-item:hover { + border-color: var(--gg-primary); + background: rgba(5, 150, 105, 0.04); +} + +.result-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.result-name { + font-size: 14px; + font-weight: 600; + color: var(--gg-text); +} + +.result-meta { + font-size: 12px; + color: var(--gg-text-muted); +} + +.result-tags { + flex-shrink: 0; +} + +/* ── 群组预览 ── */ .group-preview { padding: 16px; background: var(--gg-bg-card); @@ -174,4 +374,32 @@ function reset() { background: rgba(5, 150, 105, 0.12); color: var(--gg-primary); } + +.tag-joined { + display: inline-block; + padding: 3px 10px; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + background: var(--gg-bg-elevated); + color: var(--gg-text-muted); +} + +.back-btn { + display: block; + width: 100%; + margin-top: 8px; + padding: 8px; + border: none; + background: transparent; + color: var(--gg-text-muted); + font-size: 13px; + cursor: pointer; + border-radius: var(--gg-radius-sm); + transition: color 0.2s; +} + +.back-btn:hover { + color: var(--gg-text); +} diff --git a/frontend/src/components/team/IdleMembersList.vue b/frontend/src/components/team/IdleMembersList.vue index 0359788..7b7980f 100644 --- a/frontend/src/components/team/IdleMembersList.vue +++ b/frontend/src/components/team/IdleMembersList.vue @@ -173,6 +173,6 @@ async function inviteMember(userId: string, username: string) { .invite-btn:hover { opacity: 0.9; - box-shadow: 0 0 12px rgba(99, 102, 241, 0.3); + box-shadow: 0 0 12px rgba(5, 150, 105, 0.3); } diff --git a/frontend/src/components/team/InvitationCard.vue b/frontend/src/components/team/InvitationCard.vue index 1361076..e3346ca 100644 --- a/frontend/src/components/team/InvitationCard.vue +++ b/frontend/src/components/team/InvitationCard.vue @@ -94,7 +94,7 @@ async function rejectInvitation() { .invitation-card:hover { border-color: var(--gg-primary); - box-shadow: 0 0 16px rgba(99, 102, 241, 0.12); + box-shadow: 0 0 16px rgba(5, 150, 105, 0.12); } .invitation-header { @@ -211,6 +211,6 @@ async function rejectInvitation() { .reject-input textarea:focus { outline: none; border-color: var(--gg-primary); - box-shadow: 0 0 8px rgba(99, 102, 241, 0.15); + box-shadow: 0 0 8px rgba(5, 150, 105, 0.15); } diff --git a/frontend/src/components/team/TeamSessionPanel.vue b/frontend/src/components/team/TeamSessionPanel.vue index 3016eb3..fd04cb1 100644 --- a/frontend/src/components/team/TeamSessionPanel.vue +++ b/frontend/src/components/team/TeamSessionPanel.vue @@ -133,7 +133,7 @@ async function handleGameSelected(gameName: string) { .team-session-panel { background: var(--gg-bg-card); border: 1px solid var(--gg-primary); - box-shadow: 0 0 16px rgba(99, 102, 241, 0.15); + box-shadow: 0 0 16px rgba(5, 150, 105, 0.15); border-radius: var(--gg-radius-md); padding: 20px; } diff --git a/frontend/src/views/GamesLibrary.vue b/frontend/src/views/GamesLibrary.vue index 5e15ff5..b23b1c5 100644 --- a/frontend/src/views/GamesLibrary.vue +++ b/frontend/src/views/GamesLibrary.vue @@ -1,6 +1,6 @@