Skip to content

Commit f30a5c0

Browse files
committed
refactor: user menu
1 parent 0ba4488 commit f30a5c0

File tree

6 files changed

+121
-138
lines changed

6 files changed

+121
-138
lines changed

apps/client/app.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@ export default defineAppConfig({
1111
},
1212
background: "dark:bg-gray-800",
1313
},
14+
slideover: {
15+
overlay: {
16+
background: "bg-black/75 dark:bg-black/75",
17+
},
18+
background: "bg-white dark:bg-gray-800",
19+
},
1420
},
1521
});
Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<template>
22
<div>
3-
<span
3+
<UIcon
44
v-if="userStore.isFounderMembership()"
5-
class="i-ph-crown-simple-fill relative overflow-hidden bg-yellow-400"
5+
name="i-ph-crown-simple-fill"
6+
class="glimmer relative overflow-hidden bg-yellow-400"
67
title="尊贵的创始会员,感谢您对 Earthworm 的大力支持!"
78
style="width: 20px; height: 20px"
89
>
9-
<div class="glimmer"></div>
10-
</span>
10+
</UIcon>
1111
</div>
1212
</template>
1313

@@ -19,40 +19,15 @@ const userStore = useUserStore();
1919

2020
<style scoped>
2121
.glimmer {
22-
position: absolute;
23-
top: 0;
24-
left: 0;
25-
width: 100%;
26-
height: 100%;
27-
background: linear-gradient(
28-
90deg,
29-
rgba(255, 255, 255, 0) 0%,
30-
rgba(139, 0, 0, 0.5) 50%,
31-
rgba(255, 255, 255, 0) 100%
32-
);
33-
animation: glimmer 2s infinite;
22+
background: linear-gradient(-45deg, #ffd700 40%, #fafafa 50%, #ffd700 60%);
23+
background-size: 300%;
24+
background-position-x: 100%;
25+
animation: shimmer 2s infinite;
3426
}
3527
36-
@keyframes glimmer {
37-
0% {
38-
transform: translateX(-100%);
28+
@keyframes shimmer {
29+
to {
30+
background-position-x: 0%;
3931
}
40-
100% {
41-
transform: translateX(100%);
42-
}
43-
}
44-
45-
/* 添加浏览器前缀 做浏览器的兼容 */
46-
@-webkit-keyframes glimmer {
47-
0% {
48-
-webkit-transform: translateX(-100%);
49-
}
50-
100% {
51-
-webkit-transform: translateX(100%);
52-
}
53-
}
54-
55-
.glimmer {
56-
-webkit-animation: glimmer 2s infinite;
5732
}
5833
</style>

apps/client/components/Navbar.vue

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@
5656
>
5757
<div
5858
class="h-8 w-8 cursor-pointer overflow-hidden rounded-full bg-gray-300 transition-all hover:scale-125 hover:opacity-90 dark:bg-gray-700"
59-
@click="handleShowUserMenu"
59+
@click="openUserMenu"
6060
>
61-
<img
62-
class="h-full object-cover"
61+
<UAvatar
6362
:src="userStore.user?.avatar"
63+
alt="Avatar"
6464
/>
6565
</div>
6666
</div>
@@ -77,32 +77,24 @@
7777
</div>
7878
</div>
7979
</header>
80-
81-
<UserMenu
82-
v-model:open="isOpenUserMenu"
83-
@logout="handleLogout"
84-
/>
8580
</template>
8681

8782
<script setup lang="ts">
8883
import { useWindowScroll } from "@vueuse/core";
89-
import { useModal } from "#imports";
9084
import { useRuntimeConfig } from "nuxt/app";
91-
import { computed, ref } from "vue";
85+
import { computed } from "vue";
9286
import { useRoute } from "vue-router";
9387
94-
import Dialog from "~/components/common/Dialog.vue";
95-
import { isAuthenticated, signIn, signOut } from "~/services/auth";
88+
import { useUserMenu } from "~/composables/user/useUserMenu";
89+
import { isAuthenticated, signIn } from "~/services/auth";
9690
import { useUserStore } from "~/store/user";
9791
9892
const runtimeConfig = useRuntimeConfig();
93+
const { openUserMenu } = useUserMenu();
9994
10095
const route = useRoute();
10196
const userStore = useUserStore();
10297
const { y } = useWindowScroll();
103-
const modal = useModal();
104-
105-
const isOpenUserMenu = ref(false);
10698
10799
const SCROLL_THRESHOLD = 8;
108100
// https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#%E5%B1%9E%E6%80%A7
@@ -123,20 +115,4 @@ const isStickyNavBar = computed(() =>
123115
["index", "User-Setting", "mastered-elements"].includes(route.name as string),
124116
);
125117
const isScrolled = computed(() => y.value >= SCROLL_THRESHOLD);
126-
127-
function handleLogout() {
128-
modal.open(Dialog, {
129-
title: "退出登录",
130-
content: "是否确认退出登录?",
131-
showCancel: true,
132-
showConfirm: true,
133-
async onConfirm() {
134-
signOut();
135-
},
136-
});
137-
}
138-
139-
function handleShowUserMenu() {
140-
isOpenUserMenu.value = true;
141-
}
142118
</script>

apps/client/components/UserMenu.vue

Lines changed: 73 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,75 @@
11
<template>
2-
<Teleport to="body">
3-
<div class="drawer drawer-end z-10">
4-
<input
5-
id="my-drawer"
6-
type="checkbox"
7-
class="drawer-toggle"
8-
v-model="open"
9-
/>
10-
<div class="drawer-content"></div>
11-
<div class="drawer-side">
12-
<label
13-
for="my-drawer"
14-
aria-label="close sidebar"
15-
class="drawer-overlay"
16-
></label>
17-
<aside class="menu min-h-full w-80 bg-base-200 p-4 text-base-content">
18-
<div class="flex items-center justify-between pb-5">
19-
<div class="flex items-center gap-3">
20-
<div class="avatar">
21-
<div class="mask mask-squircle h-12 w-12">
22-
<img
23-
:src="userStore.user?.avatar"
24-
alt="Avatar Tailwind CSS Component"
25-
/>
26-
</div>
27-
</div>
28-
<div>
29-
<div class="flex gap-2">
30-
<div class="text-xl font-bold">{{ userStore.user?.username }}</div>
31-
<MembershipBadge></MembershipBadge>
32-
</div>
33-
<div class="text-sm opacity-50">{{ userStore.user?.name }}</div>
34-
</div>
2+
<USlideover
3+
v-model="isUserMenuOpen"
4+
:ui="{ width: 'w-screen max-w-80', strategy: 'override' }"
5+
>
6+
<div class="flex h-full flex-col">
7+
<!-- 用户信息头部 -->
8+
<div class="flex items-center justify-between p-4">
9+
<div class="flex items-center gap-3">
10+
<div class="avatar">
11+
<div class="mask mask-squircle h-14 w-14">
12+
<UAvatar
13+
size="xl"
14+
:src="userStore.user?.avatar"
15+
alt="Avatar"
16+
/>
3517
</div>
36-
37-
<div>
38-
<label
39-
for="my-drawer"
40-
class="btn btn-square btn-ghost drawer-button btn-sm"
41-
>
42-
<span class="i-ph-x-bold h-6 w-6"></span>
43-
</label>
18+
</div>
19+
<div>
20+
<div class="flex gap-2">
21+
<div class="text-xl font-bold">{{ userStore.user?.username }}</div>
22+
<MembershipBadge></MembershipBadge>
4423
</div>
24+
<div class="text-sm opacity-75">{{ userStore.user?.name }}</div>
4525
</div>
26+
</div>
27+
28+
<UButton
29+
color="gray"
30+
variant="ghost"
31+
icon="i-heroicons-x-mark-20-solid"
32+
@click="closeUserMenu"
33+
tabindex="-1"
34+
:ui="{ color: { gray: { ghost: 'dark:hover:bg-gray-600' } } }"
35+
/>
36+
</div>
4637

47-
<ul>
48-
<li
49-
v-for="(item, index) in showMenuOptions"
50-
:index="index"
51-
:key="item.name"
52-
>
53-
<span
54-
@click="item.eventName"
55-
class=""
56-
>
57-
<span
58-
class="h-6 w-6"
59-
:class="item.icon"
60-
></span>
61-
<span class="text-sm font-medium">
62-
{{ item.title }}
63-
</span>
64-
</span>
65-
</li>
66-
</ul>
67-
</aside>
38+
<!-- 菜单选项 -->
39+
<div class="flex-grow p-4">
40+
<button
41+
v-for="(item, index) in showMenuOptions"
42+
:key="item.name"
43+
@click="item.eventName"
44+
class="mb-2 flex w-full items-center rounded-lg p-3 transition-all duration-200 ease-in-out hover:bg-base-200 hover:shadow-md dark:hover:bg-gray-600"
45+
tabindex="-1"
46+
>
47+
<UIcon
48+
:name="item.icon"
49+
class="mr-3 h-7 w-7"
50+
></UIcon>
51+
<span class="text-lg font-medium">{{ item.title }}</span>
52+
</button>
6853
</div>
54+
55+
<!-- 底部信息 -->
56+
<div class="p-4 text-center text-xs opacity-50">版本 v1.0.0</div>
6957
</div>
70-
</Teleport>
58+
</USlideover>
7159
</template>
7260

7361
<script setup lang="ts">
74-
import { navigateTo } from "#imports";
62+
import { navigateTo, useModal } from "#imports";
7563
import { useRuntimeConfig } from "nuxt/app";
7664
import { computed } from "vue";
7765
66+
import Dialog from "~/components/common/Dialog.vue";
7867
import { Theme, useDarkMode } from "~/composables/darkMode";
68+
import { useUserMenu } from "~/composables/user/useUserMenu";
69+
import { signOut } from "~/services/auth";
7970
import { useUserStore } from "~/store/user";
8071
72+
const { isUserMenuOpen, closeUserMenu } = useUserMenu();
8173
const { darkMode, toggleDarkMode } = useDarkMode();
8274
8375
const runtimeConfig = useRuntimeConfig();
@@ -87,6 +79,7 @@ const open = defineModel("open");
8779
8880
const userStore = useUserStore();
8981
const isDarkMode = computed(() => darkMode.value === Theme.DARK);
82+
const modal = useModal();
9083
9184
const showMenuOptions = computed(() => {
9285
return [
@@ -136,32 +129,41 @@ const showMenuOptions = computed(() => {
136129
});
137130
138131
function handleHelpDocs() {
139-
open.value = false;
132+
closeUserMenu();
140133
window.open(runtimeConfig.public.helpDocsURL, "_blank");
141134
}
142135
143136
function handleFeedback() {
144-
open.value = false;
137+
closeUserMenu();
145138
window.open("https://txc.qq.com/products/652508", "_blank");
146139
}
147140
148141
function handleMasteredElements() {
149-
open.value = false;
142+
closeUserMenu();
150143
navigateTo("/mastered-elements");
151144
}
152145
153146
function handleSetting() {
154-
open.value = false;
147+
closeUserMenu();
155148
navigateTo("/user/setting");
156149
}
157150
158151
function handleLogout() {
159-
open.value = false;
160-
emit("logout", true);
152+
closeUserMenu();
153+
154+
modal.open(Dialog, {
155+
title: "退出登录",
156+
content: "是否确认退出登录?",
157+
showCancel: true,
158+
showConfirm: true,
159+
async onConfirm() {
160+
signOut();
161+
},
162+
});
161163
}
162164
163165
function handleGoToEditor() {
164-
open.value = false;
166+
closeUserMenu();
165167
window.open("https://earthworm-editor.cuixueshe.com", "_blank");
166168
}
167169
</script>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ref } from "vue";
2+
3+
const isUserMenuOpen = ref(false);
4+
export function useUserMenu() {
5+
const openUserMenu = () => {
6+
isUserMenuOpen.value = true;
7+
};
8+
9+
const closeUserMenu = () => {
10+
isUserMenuOpen.value = false;
11+
};
12+
13+
const toggleUserMenu = () => {
14+
isUserMenuOpen.value = !isUserMenuOpen.value;
15+
};
16+
17+
return {
18+
isUserMenuOpen,
19+
openUserMenu,
20+
closeUserMenu,
21+
toggleUserMenu,
22+
};
23+
}

apps/client/layouts/default.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<Footer></Footer>
1515
</div>
1616
</div>
17+
<UserMenu />
1718
</template>
1819

1920
<script setup lang="ts">

0 commit comments

Comments
 (0)