Skip to content

Commit

Permalink
refactor: user menu
Browse files Browse the repository at this point in the history
  • Loading branch information
cuixiaorui committed Aug 12, 2024
1 parent 0ba4488 commit f30a5c0
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 138 deletions.
6 changes: 6 additions & 0 deletions apps/client/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ export default defineAppConfig({
},
background: "dark:bg-gray-800",
},
slideover: {
overlay: {
background: "bg-black/75 dark:bg-black/75",
},
background: "bg-white dark:bg-gray-800",
},
},
});
47 changes: 11 additions & 36 deletions apps/client/components/MembershipBadge.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<div>
<span
<UIcon
v-if="userStore.isFounderMembership()"
class="i-ph-crown-simple-fill relative overflow-hidden bg-yellow-400"
name="i-ph-crown-simple-fill"
class="glimmer relative overflow-hidden bg-yellow-400"
title="尊贵的创始会员,感谢您对 Earthworm 的大力支持!"
style="width: 20px; height: 20px"
>
<div class="glimmer"></div>
</span>
</UIcon>
</div>
</template>

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

<style scoped>
.glimmer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(139, 0, 0, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
animation: glimmer 2s infinite;
background: linear-gradient(-45deg, #ffd700 40%, #fafafa 50%, #ffd700 60%);
background-size: 300%;
background-position-x: 100%;
animation: shimmer 2s infinite;
}
@keyframes glimmer {
0% {
transform: translateX(-100%);
@keyframes shimmer {
to {
background-position-x: 0%;
}
100% {
transform: translateX(100%);
}
}
/* 添加浏览器前缀 做浏览器的兼容 */
@-webkit-keyframes glimmer {
0% {
-webkit-transform: translateX(-100%);
}
100% {
-webkit-transform: translateX(100%);
}
}
.glimmer {
-webkit-animation: glimmer 2s infinite;
}
</style>
38 changes: 7 additions & 31 deletions apps/client/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@
>
<div
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"
@click="handleShowUserMenu"
@click="openUserMenu"
>
<img
class="h-full object-cover"
<UAvatar
:src="userStore.user?.avatar"
alt="Avatar"
/>
</div>
</div>
Expand All @@ -77,32 +77,24 @@
</div>
</div>
</header>

<UserMenu
v-model:open="isOpenUserMenu"
@logout="handleLogout"
/>
</template>

<script setup lang="ts">
import { useWindowScroll } from "@vueuse/core";
import { useModal } from "#imports";
import { useRuntimeConfig } from "nuxt/app";
import { computed, ref } from "vue";
import { computed } from "vue";
import { useRoute } from "vue-router";
import Dialog from "~/components/common/Dialog.vue";
import { isAuthenticated, signIn, signOut } from "~/services/auth";
import { useUserMenu } from "~/composables/user/useUserMenu";
import { isAuthenticated, signIn } from "~/services/auth";
import { useUserStore } from "~/store/user";
const runtimeConfig = useRuntimeConfig();
const { openUserMenu } = useUserMenu();
const route = useRoute();
const userStore = useUserStore();
const { y } = useWindowScroll();
const modal = useModal();
const isOpenUserMenu = ref(false);
const SCROLL_THRESHOLD = 8;
// https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#%E5%B1%9E%E6%80%A7
Expand All @@ -123,20 +115,4 @@ const isStickyNavBar = computed(() =>
["index", "User-Setting", "mastered-elements"].includes(route.name as string),
);
const isScrolled = computed(() => y.value >= SCROLL_THRESHOLD);
function handleLogout() {
modal.open(Dialog, {
title: "退出登录",
content: "是否确认退出登录?",
showCancel: true,
showConfirm: true,
async onConfirm() {
signOut();
},
});
}
function handleShowUserMenu() {
isOpenUserMenu.value = true;
}
</script>
144 changes: 73 additions & 71 deletions apps/client/components/UserMenu.vue
Original file line number Diff line number Diff line change
@@ -1,83 +1,75 @@
<template>
<Teleport to="body">
<div class="drawer drawer-end z-10">
<input
id="my-drawer"
type="checkbox"
class="drawer-toggle"
v-model="open"
/>
<div class="drawer-content"></div>
<div class="drawer-side">
<label
for="my-drawer"
aria-label="close sidebar"
class="drawer-overlay"
></label>
<aside class="menu min-h-full w-80 bg-base-200 p-4 text-base-content">
<div class="flex items-center justify-between pb-5">
<div class="flex items-center gap-3">
<div class="avatar">
<div class="mask mask-squircle h-12 w-12">
<img
:src="userStore.user?.avatar"
alt="Avatar Tailwind CSS Component"
/>
</div>
</div>
<div>
<div class="flex gap-2">
<div class="text-xl font-bold">{{ userStore.user?.username }}</div>
<MembershipBadge></MembershipBadge>
</div>
<div class="text-sm opacity-50">{{ userStore.user?.name }}</div>
</div>
<USlideover
v-model="isUserMenuOpen"
:ui="{ width: 'w-screen max-w-80', strategy: 'override' }"
>
<div class="flex h-full flex-col">
<!-- 用户信息头部 -->
<div class="flex items-center justify-between p-4">
<div class="flex items-center gap-3">
<div class="avatar">
<div class="mask mask-squircle h-14 w-14">
<UAvatar
size="xl"
:src="userStore.user?.avatar"
alt="Avatar"
/>
</div>

<div>
<label
for="my-drawer"
class="btn btn-square btn-ghost drawer-button btn-sm"
>
<span class="i-ph-x-bold h-6 w-6"></span>
</label>
</div>
<div>
<div class="flex gap-2">
<div class="text-xl font-bold">{{ userStore.user?.username }}</div>
<MembershipBadge></MembershipBadge>
</div>
<div class="text-sm opacity-75">{{ userStore.user?.name }}</div>
</div>
</div>

<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
@click="closeUserMenu"
tabindex="-1"
:ui="{ color: { gray: { ghost: 'dark:hover:bg-gray-600' } } }"
/>
</div>

<ul>
<li
v-for="(item, index) in showMenuOptions"
:index="index"
:key="item.name"
>
<span
@click="item.eventName"
class=""
>
<span
class="h-6 w-6"
:class="item.icon"
></span>
<span class="text-sm font-medium">
{{ item.title }}
</span>
</span>
</li>
</ul>
</aside>
<!-- 菜单选项 -->
<div class="flex-grow p-4">
<button
v-for="(item, index) in showMenuOptions"
:key="item.name"
@click="item.eventName"
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"
tabindex="-1"
>
<UIcon
:name="item.icon"
class="mr-3 h-7 w-7"
></UIcon>
<span class="text-lg font-medium">{{ item.title }}</span>
</button>
</div>

<!-- 底部信息 -->
<div class="p-4 text-center text-xs opacity-50">版本 v1.0.0</div>
</div>
</Teleport>
</USlideover>
</template>

<script setup lang="ts">
import { navigateTo } from "#imports";
import { navigateTo, useModal } from "#imports";
import { useRuntimeConfig } from "nuxt/app";
import { computed } from "vue";
import Dialog from "~/components/common/Dialog.vue";
import { Theme, useDarkMode } from "~/composables/darkMode";
import { useUserMenu } from "~/composables/user/useUserMenu";
import { signOut } from "~/services/auth";
import { useUserStore } from "~/store/user";
const { isUserMenuOpen, closeUserMenu } = useUserMenu();
const { darkMode, toggleDarkMode } = useDarkMode();
const runtimeConfig = useRuntimeConfig();
Expand All @@ -87,6 +79,7 @@ const open = defineModel("open");
const userStore = useUserStore();
const isDarkMode = computed(() => darkMode.value === Theme.DARK);
const modal = useModal();
const showMenuOptions = computed(() => {
return [
Expand Down Expand Up @@ -136,32 +129,41 @@ const showMenuOptions = computed(() => {
});
function handleHelpDocs() {
open.value = false;
closeUserMenu();
window.open(runtimeConfig.public.helpDocsURL, "_blank");
}
function handleFeedback() {
open.value = false;
closeUserMenu();
window.open("https://txc.qq.com/products/652508", "_blank");
}
function handleMasteredElements() {
open.value = false;
closeUserMenu();
navigateTo("/mastered-elements");
}
function handleSetting() {
open.value = false;
closeUserMenu();
navigateTo("/user/setting");
}
function handleLogout() {
open.value = false;
emit("logout", true);
closeUserMenu();
modal.open(Dialog, {
title: "退出登录",
content: "是否确认退出登录?",
showCancel: true,
showConfirm: true,
async onConfirm() {
signOut();
},
});
}
function handleGoToEditor() {
open.value = false;
closeUserMenu();
window.open("https://earthworm-editor.cuixueshe.com", "_blank");
}
</script>
Expand Down
23 changes: 23 additions & 0 deletions apps/client/composables/user/useUserMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ref } from "vue";

const isUserMenuOpen = ref(false);
export function useUserMenu() {
const openUserMenu = () => {
isUserMenuOpen.value = true;
};

const closeUserMenu = () => {
isUserMenuOpen.value = false;
};

const toggleUserMenu = () => {
isUserMenuOpen.value = !isUserMenuOpen.value;
};

return {
isUserMenuOpen,
openUserMenu,
closeUserMenu,
toggleUserMenu,
};
}
1 change: 1 addition & 0 deletions apps/client/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Footer></Footer>
</div>
</div>
<UserMenu />
</template>

<script setup lang="ts">
Expand Down

0 comments on commit f30a5c0

Please sign in to comment.