mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
feat: add option to use old top bar
This commit is contained in:
@@ -80,6 +80,7 @@ settings:
|
||||
block_ads: 屏蔽广告
|
||||
disable_frosted_glass: 禁用毛玻璃效果
|
||||
reduce_frosted_glass_blur: 降低毛玻璃模糊强度
|
||||
use_old_topbar: 使用旧版顶栏
|
||||
auto_hide_topbar: 自动隐藏顶栏
|
||||
topbar_icon_badges: 图标角标
|
||||
topbar_icon_badges_opt:
|
||||
|
||||
@@ -80,6 +80,7 @@ settings:
|
||||
block_ads: 封鎖廣告
|
||||
disable_frosted_glass: 停用毛玻璃效果
|
||||
reduce_frosted_glass_blur: 降低毛玻璃模糊強度
|
||||
use_old_topbar: 使用舊版頂欄
|
||||
auto_hide_topbar: 自動隱藏頂欄
|
||||
topbar_icon_badges: 圖示標記樣式
|
||||
topbar_icon_badges_opt:
|
||||
|
||||
@@ -80,6 +80,7 @@ settings:
|
||||
block_ads: Block ads
|
||||
disable_frosted_glass: Disable frosted glass effect
|
||||
reduce_frosted_glass_blur: Reduce the intensity of the frosted glass blur
|
||||
use_old_topbar: Use the old top bar
|
||||
auto_hide_topbar: Automatically hide the top bar
|
||||
topbar_icon_badges: Icon badges
|
||||
topbar_icon_badges_opt:
|
||||
|
||||
@@ -80,6 +80,7 @@ settings:
|
||||
block_ads: 封鎖廣告
|
||||
disable_frosted_glass: 閂咗毛玻璃效果
|
||||
reduce_frosted_glass_blur: 降低毛玻璃模糊強度
|
||||
use_old_topbar: 用返舊版頂欄
|
||||
auto_hide_topbar: 自動收埋頂欄
|
||||
topbar_icon_badges: 圖示邊位標記
|
||||
topbar_icon_badges_opt:
|
||||
|
||||
@@ -156,6 +156,9 @@ function handleToggleDockItem(dockItem: any) {
|
||||
</SettingsItemGroup>
|
||||
|
||||
<SettingsItemGroup :title="$t('settings.group_topbar')">
|
||||
<SettingsItem :title="$t('settings.use_old_topbar')">
|
||||
<Radio v-model="settings.useOldTopBar" />
|
||||
</SettingsItem>
|
||||
<SettingsItem :title="$t('settings.auto_hide_topbar')">
|
||||
<Radio v-model="settings.autoHideTopBar" />
|
||||
</SettingsItem>
|
||||
|
||||
957
src/components/TopBar/OldTopBar.vue
Normal file
957
src/components/TopBar/OldTopBar.vue
Normal file
@@ -0,0 +1,957 @@
|
||||
<script setup lang="ts">
|
||||
import { useMouseInElement } from '@vueuse/core'
|
||||
import type { Ref, UnwrapNestedRefs } from 'vue'
|
||||
|
||||
import { useApiClient } from '~/composables/api'
|
||||
import { useBewlyApp } from '~/composables/useAppProvider'
|
||||
import { useDelayedHover } from '~/composables/useDelayedHover'
|
||||
import { OVERLAY_SCROLL_BAR_SCROLL, TOP_BAR_VISIBILITY_CHANGE } from '~/constants/globalEvents'
|
||||
import { AppPage } from '~/enums/appEnums'
|
||||
import { settings } from '~/logic'
|
||||
import { getUserID, isHomePage } from '~/utils/main'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
import SearchBar from '../SearchBar/SearchBar.vue'
|
||||
import ChannelsPop from './components/ChannelsPop.vue'
|
||||
import FavoritesPop from './components/FavoritesPop.vue'
|
||||
import HistoryPop from './components/HistoryPop.vue'
|
||||
import MomentsPop from './components/MomentsPop.vue'
|
||||
import MorePop from './components/MorePop.vue'
|
||||
import NotificationsPop from './components/NotificationsPop.vue'
|
||||
import UploadPop from './components/UploadPop.vue'
|
||||
import WatchLaterPop from './components/WatchLaterPop.vue'
|
||||
import { updateInterval } from './notify'
|
||||
import OldUserPanelPop from './oldTopBarComponents/OldUserPanelPop.vue'
|
||||
import type { UnReadDm, UnReadMessage, UserInfo } from './types'
|
||||
|
||||
// import { useTopBarStore } from '~/stores/topBarStore'
|
||||
|
||||
// const popups = { NotificationsPop, MomentsPop, FavoritesPop, HistoryPop }
|
||||
|
||||
// const topBarStore = useTopBarStore()
|
||||
|
||||
// const topBarItems = computed(() => {
|
||||
// return topBarStore.topBarItems
|
||||
// })
|
||||
|
||||
const { activatedPage, scrollbarRef, reachTop } = useBewlyApp()
|
||||
|
||||
const mid = getUserID() || ''
|
||||
const userInfo = reactive<UserInfo | NonNullable<unknown>>({}) as UnwrapNestedRefs<UserInfo>
|
||||
|
||||
const hideTopBar = ref<boolean>(false)
|
||||
const headerTarget = ref(null)
|
||||
const { isOutside: isOutsideTopBar } = useMouseInElement(headerTarget)
|
||||
|
||||
const api = useApiClient()
|
||||
|
||||
// initially, assume the user is logged in cuz data retrieval is slow, which may show the login
|
||||
// button even after login. if the user is not logged in, the login button will show up later
|
||||
const isLogin = ref<boolean>(true)
|
||||
|
||||
const logo = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const avatarImg = ref<HTMLImageElement>() as Ref<HTMLImageElement>
|
||||
const avatarShadow = ref<HTMLImageElement>() as Ref<HTMLImageElement>
|
||||
const favoritesPopRef = ref<any>()
|
||||
const momentsPopRef = ref()
|
||||
|
||||
const scrollTop = ref<number>(0)
|
||||
const oldScrollTop = ref<number>(0)
|
||||
|
||||
const isSearchPage = computed(() => {
|
||||
if (/https?:\/\/search.bilibili.com\/.*$/.test(location.href))
|
||||
return true
|
||||
return false
|
||||
})
|
||||
|
||||
const showSearchBar = computed(() => {
|
||||
if (isHomePage()) {
|
||||
if (settings.value.useOriginalBilibiliHomepage)
|
||||
return true
|
||||
if (activatedPage.value === AppPage.Search)
|
||||
return false
|
||||
if (settings.value.useSearchPageModeOnHomePage && activatedPage.value === AppPage.Home && reachTop.value)
|
||||
return false
|
||||
}
|
||||
else {
|
||||
if (isSearchPage.value)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const isTopBarFixed = computed(() => {
|
||||
if (
|
||||
(isHomePage() && settings.value.useOriginalBilibiliHomepage)
|
||||
// video page
|
||||
|| /https?:\/\/(?:www.)?bilibili.com\/(?:video|list)\/.*/.test(location.href)
|
||||
// anime playback & movie page
|
||||
|| /https?:\/\/(?:www.)?bilibili.com\/bangumi\/play\/.*/.test(location.href)
|
||||
// moment page
|
||||
|| /https?:\/\/t.bilibili.com.*/.test(location.href)
|
||||
// channel, anime, chinese anime, tv shows, movie, variety shows, mooc
|
||||
|| /https?:\/\/(?:www.)?bilibili.com\/(?:v|anime|guochuang|tv|movie|variety|mooc).*/.test(location.href)
|
||||
// articles page
|
||||
|| /https?:\/\/(?:www.)?bilibili.com\/read\/home.*/.test(location.href)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const showTopBar = computed(() => {
|
||||
const isCreativeCenter = /https?:\/\/member.bilibili.com\/platform.*/.test(location.href)
|
||||
if (settings.value.showTopBar && !isCreativeCenter)
|
||||
return true
|
||||
return false
|
||||
})
|
||||
|
||||
// #region Popups visibility control
|
||||
const popupVisible = reactive({
|
||||
channels: false,
|
||||
userPanel: false,
|
||||
notifications: false,
|
||||
moments: false,
|
||||
favorites: false,
|
||||
history: false,
|
||||
watchLater: false,
|
||||
upload: false,
|
||||
more: false,
|
||||
})
|
||||
|
||||
function closeAllTopBarPopup(exceptionKey?: keyof typeof popupVisible) {
|
||||
Object.keys(popupVisible).forEach((key) => {
|
||||
if (key !== exceptionKey)
|
||||
popupVisible[key as keyof typeof popupVisible] = false
|
||||
})
|
||||
}
|
||||
|
||||
const rightSideInactive = computed(() => {
|
||||
let isInactive = false
|
||||
Object.entries(popupVisible).forEach(([key, value]) => {
|
||||
if (value && key !== 'userPanel') {
|
||||
isInactive = true
|
||||
}
|
||||
})
|
||||
return isInactive
|
||||
})
|
||||
|
||||
// Channels
|
||||
const channels = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('channels'),
|
||||
enter: () => {
|
||||
popupVisible.channels = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.channels = false
|
||||
},
|
||||
})
|
||||
|
||||
// Avatar
|
||||
const avatar = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('userPanel'),
|
||||
enter: () => {
|
||||
popupVisible.userPanel = true
|
||||
},
|
||||
beforeLeave: () => {
|
||||
popupVisible.userPanel = false
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.userPanel = false
|
||||
},
|
||||
})
|
||||
|
||||
// Notifications
|
||||
const notifications = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('notifications'),
|
||||
enter: () => {
|
||||
popupVisible.notifications = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.notifications = false
|
||||
},
|
||||
})
|
||||
|
||||
// Moments
|
||||
const moments = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('moments'),
|
||||
enter: () => {
|
||||
popupVisible.moments = true
|
||||
momentsPopRef.value && momentsPopRef.value.checkIfHasNewMomentsThenUpdateMoments()
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.moments = false
|
||||
},
|
||||
})
|
||||
|
||||
// Favorites
|
||||
const favorites = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('favorites'),
|
||||
enter: () => {
|
||||
popupVisible.favorites = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.favorites = false
|
||||
},
|
||||
})
|
||||
|
||||
// History
|
||||
const history = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('history'),
|
||||
enter: () => {
|
||||
popupVisible.history = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.history = false
|
||||
},
|
||||
})
|
||||
|
||||
// Watch Later
|
||||
const watchLater = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('watchLater'),
|
||||
enter: () => {
|
||||
popupVisible.watchLater = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.watchLater = false
|
||||
},
|
||||
})
|
||||
|
||||
// Upload
|
||||
const upload = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup('upload'),
|
||||
enter: () => {
|
||||
popupVisible.upload = true
|
||||
},
|
||||
leave: () => {
|
||||
popupVisible.upload = false
|
||||
},
|
||||
})
|
||||
|
||||
// More
|
||||
const more = useDelayedHover({
|
||||
beforeEnter: () => closeAllTopBarPopup(),
|
||||
enter: () => {
|
||||
popupVisible.more = true
|
||||
},
|
||||
leave: () => popupVisible.more = false,
|
||||
})
|
||||
|
||||
// #endregion
|
||||
|
||||
watch(() => settings.value.autoHideTopBar, (newVal) => {
|
||||
if (!newVal)
|
||||
toggleTopBarVisible(true)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => popupVisible.notifications,
|
||||
(newVal, oldVal) => {
|
||||
// Stop loading new message counts on the message page, because it resets to 0 when the
|
||||
// users reads the messages on this page
|
||||
if (oldVal === undefined && /https?:\/\/message.bilibili.com.*$/.test(location.href))
|
||||
return
|
||||
|
||||
if (newVal === oldVal)
|
||||
return
|
||||
|
||||
if (!newVal)
|
||||
getUnreadMessageCount()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => popupVisible.moments,
|
||||
async (newVal, oldVal) => {
|
||||
if (newVal === oldVal)
|
||||
return
|
||||
|
||||
if (!newVal)
|
||||
await getTopBarNewMomentsCount()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(() => popupVisible.favorites, (newVal, oldVal) => {
|
||||
if (newVal === oldVal)
|
||||
return
|
||||
if (newVal) {
|
||||
nextTick(() => {
|
||||
if (favoritesPopRef.value)
|
||||
favoritesPopRef.value.refreshFavoriteResources()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
watch(activatedPage, () => {
|
||||
toggleTopBarVisible(true)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
initData()
|
||||
await nextTick()
|
||||
toggleTopBarVisible(true)
|
||||
|
||||
emitter.off(OVERLAY_SCROLL_BAR_SCROLL)
|
||||
if (isHomePage() && !settings.value.useOriginalBilibiliHomepage) {
|
||||
emitter.on(OVERLAY_SCROLL_BAR_SCROLL, () => {
|
||||
handleScroll()
|
||||
})
|
||||
}
|
||||
else {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
emitter.off(OVERLAY_SCROLL_BAR_SCROLL)
|
||||
})
|
||||
|
||||
async function initData() {
|
||||
await getUserInfo()
|
||||
|
||||
// automatically update notifications and moments count
|
||||
setInterval(() => {
|
||||
getUnreadMessageCount()
|
||||
getTopBarNewMomentsCount()
|
||||
}, updateInterval)
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
if (isHomePage() && !settings.value.useOriginalBilibiliHomepage) {
|
||||
const osInstance = scrollbarRef.value?.osInstance()
|
||||
scrollTop.value = osInstance.elements().viewport.scrollTop as number
|
||||
}
|
||||
else {
|
||||
scrollTop.value = document.documentElement.scrollTop
|
||||
}
|
||||
|
||||
if (scrollTop.value === 0)
|
||||
toggleTopBarVisible(true)
|
||||
|
||||
if (settings.value.autoHideTopBar && isOutsideTopBar && scrollTop.value !== 0) {
|
||||
if (scrollTop.value > oldScrollTop.value)
|
||||
toggleTopBarVisible(false)
|
||||
|
||||
else
|
||||
toggleTopBarVisible(true)
|
||||
}
|
||||
|
||||
oldScrollTop.value = scrollTop.value
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
try {
|
||||
const res = await api.user.getUserInfo()
|
||||
|
||||
if (res.code === 0) {
|
||||
isLogin.value = true
|
||||
Object.assign(userInfo, res.data)
|
||||
}
|
||||
// Account not logged in
|
||||
else if (res.code === -101) {
|
||||
isLogin.value = false
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
isLogin.value = false
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// #region Notifications
|
||||
const unReadMessage = reactive<UnReadMessage | NonNullable<unknown>>(
|
||||
{},
|
||||
) as UnwrapNestedRefs<UnReadMessage>
|
||||
const unReadDm = reactive<UnReadDm>({} as UnwrapNestedRefs<UnReadDm>)
|
||||
const unReadMessageCount = ref<number>(0)
|
||||
|
||||
async function getUnreadMessageCount() {
|
||||
if (!isLogin.value)
|
||||
return
|
||||
|
||||
let result = 0
|
||||
|
||||
try {
|
||||
let res
|
||||
res = await api.notification.getUnreadMsg()
|
||||
if (res.code === 0) {
|
||||
Object.assign(unReadMessage, res.data)
|
||||
Object.entries(unReadMessage).forEach(([key, value]) => {
|
||||
if (key !== 'up' && key !== 'recv_reply' && key !== 'recv_like') {
|
||||
if (typeof value === 'number')
|
||||
result += value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
res = await api.notification.getUnreadDm()
|
||||
if (res.code === 0) {
|
||||
Object.assign(unReadDm, res.data)
|
||||
if (typeof unReadDm.follow_unread === 'number')
|
||||
result += unReadDm.follow_unread
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
finally {
|
||||
unReadMessageCount.value = result
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Moments
|
||||
const newMomentsCount = ref<number>(0)
|
||||
|
||||
async function getTopBarNewMomentsCount() {
|
||||
if (!isLogin.value)
|
||||
return
|
||||
|
||||
let result = 0
|
||||
|
||||
try {
|
||||
const res = await api.moment.getTopBarNewMomentsCount()
|
||||
if (res.code === 0) {
|
||||
if (typeof res.data.update_info.item.count === 'number')
|
||||
result = res.data.update_info.item.count
|
||||
}
|
||||
}
|
||||
finally {
|
||||
newMomentsCount.value = result
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
function toggleTopBarVisible(visible: boolean) {
|
||||
hideTopBar.value = !visible
|
||||
emitter.emit(TOP_BAR_VISIBILITY_CHANGE, visible)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
toggleTopBarVisible,
|
||||
handleScroll,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="top-bar">
|
||||
<header
|
||||
v-if="showTopBar"
|
||||
ref="headerTarget"
|
||||
w="full" transition="all 300 ease-in-out"
|
||||
:class="{ hide: hideTopBar }"
|
||||
:style="{ position: isTopBarFixed ? 'fixed' : 'absolute' }"
|
||||
>
|
||||
<main
|
||||
max-w="$bew-page-max-width"
|
||||
flex="~ justify-between items-center gap-4"
|
||||
p="x-12" m-auto
|
||||
h="$bew-top-bar-height"
|
||||
>
|
||||
<!-- Top bar mask -->
|
||||
<div
|
||||
v-if="!reachTop"
|
||||
style="
|
||||
mask-image: linear-gradient(to bottom, black 20%, transparent);
|
||||
"
|
||||
:style="{ backdropFilter: settings.disableFrostedGlass ? 'none' : 'blur(4px)' }"
|
||||
pos="absolute top-0 left-0" w-full h-80px
|
||||
pointer-events-none transform-gpu
|
||||
/>
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="!reachTop"
|
||||
pos="absolute top-0 left-0" w-full h-80px
|
||||
pointer-events-none opacity-80
|
||||
:style="{
|
||||
background: `linear-gradient(to bottom, ${(
|
||||
settings.wallpaper
|
||||
|| settings.useSearchPageModeOnHomePage
|
||||
&& settings.searchPageWallpaper
|
||||
&& settings.individuallySetSearchPageWallpaper)
|
||||
&& isHomePage()
|
||||
? 'rgba(0,0,0,.6)' : `${isHomePage() ? 'var(--bew-homepage-bg)' : 'var(--bew-bg)'}`}, transparent)`,
|
||||
}"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<div shrink-0 flex="inline xl:1 justify-center">
|
||||
<div
|
||||
ref="channels"
|
||||
z-1 relative w-fit mr-auto
|
||||
>
|
||||
<a
|
||||
ref="logo" href="//www.bilibili.com"
|
||||
class="group logo"
|
||||
:class="{ activated: popupVisible.channels }"
|
||||
style="backdrop-filter: var(--bew-filter-glass-1);"
|
||||
grid="~ place-items-center" border="1 $bew-border-color"
|
||||
rounded="50px" duration-300
|
||||
bg="$bew-elevated hover:$bew-theme-color dark-hover:white"
|
||||
shadow="$bew-shadow-2"
|
||||
w-50px h-50px transform-gpu
|
||||
>
|
||||
|
||||
<svg
|
||||
t="1720198072316" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1477" width="40" height="40"
|
||||
un-fill="$bew-theme-color dark:white" un-group-hover:fill="white dark:$bew-theme-color"
|
||||
duration-300 mt--1px
|
||||
>
|
||||
>
|
||||
<path d="M450.803484 456.506027l-120.670435 23.103715 10.333298 45.288107 119.454151-23.102578-9.117014-45.289244z m65.04448 120.060586c-29.483236 63.220622-55.926329 15.502222-55.926328 15.502223l-19.754098 12.768142s38.90176 53.192249 75.986489 12.764729c43.770311 40.42752 77.203911-13.068516 77.203911-13.068516l-17.934791-11.55072c0.001138-0.304924-31.305956 44.983182-59.575183-16.415858z m59.57632-74.773617L695.182222 524.895573l10.029511-45.288106-120.364373-23.103716-9.423076 45.289245z m237.784178-88.926436c-1.905778-84.362809-75.487004-100.540871-75.487004-100.540871s-57.408853-0.316302-131.944676-0.95232l54.237867-52.332089s8.562916-10.784996-6.026809-22.834062c-14.592-12.051342-15.543182-6.660551-20.615396-3.487289-4.441884 3.169849-69.462471 66.920676-80.878933 78.340551-29.494613 0-60.2624-0.319716-90.075591-0.319716h10.466418s-77.705671-76.754489-82.781298-80.241777c-5.075627-3.488427-5.709369-8.56064-20.616533 3.487289-14.589724 12.05248-6.026809 22.8352-6.026809 22.8352l55.504213 53.919288c-60.261262 0-112.280462 0.319716-136.383147 1.268623-78.025387 22.521173-71.99744 100.859449-71.99744 100.859449s0.950044 168.100978 0 253.103217c8.562916 85.00224 73.899804 98.636231 73.899805 98.636231s26.007324 0.63488 45.357511 0.63488c1.900089 5.391929 3.486151 32.034133 33.302756 32.034134 29.495751 0 33.30048-32.034133 33.30048-32.034134s217.263218-0.950044 235.340231-0.950044c0.953458 9.196658 5.394204 33.619058 35.207395 33.303893 29.494613-0.636018 31.714418-35.20512 31.714418-35.20512s10.151253-0.95232 40.280747 0c70.413653-13.005938 74.534684-95.468658 74.534684-95.468657s-1.265209-169.689316-0.312889-254.056676zM752.628622 681.8304c0 13.319964-10.467556 24.102684-23.471218 24.102684H300.980907c-13.003662 0-23.47008-10.78272-23.47008-24.102684V397.961671c0-13.32224 10.467556-24.106098 23.47008-24.106098h428.176497c13.003662 0 23.471218 10.783858 23.471218 24.106098v283.868729z" p-id="1478" /></svg>
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<ChannelsPop
|
||||
v-show="popupVisible.channels"
|
||||
class="bew-popover"
|
||||
pos="!left-0 !top-70px"
|
||||
transform="!translate-x-0"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- search bar -->
|
||||
<div flex="inline 1 md:justify-center <md:justify-end items-center" w="full">
|
||||
<Transition name="slide-out">
|
||||
<SearchBar
|
||||
v-if="showSearchBar"
|
||||
style="
|
||||
--b-search-bar-color: var(--bew-elevated);
|
||||
--b-search-bar-hover: var(--bew-elevated-hover);
|
||||
"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- right content -->
|
||||
<div
|
||||
class="right-side"
|
||||
flex="inline xl:1 justify-end items-center"
|
||||
>
|
||||
<!-- Avatar -->
|
||||
<div
|
||||
v-if="isLogin"
|
||||
ref="avatar"
|
||||
class="avatar right-side-item relative"
|
||||
shadow="$bew-shadow-2" rounded-full
|
||||
>
|
||||
<a
|
||||
ref="avatarImg"
|
||||
:href="`https://space.bilibili.com/${mid}`"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
class="avatar-img"
|
||||
:class="{ hover: popupVisible.userPanel }"
|
||||
:style="{
|
||||
backgroundImage: `url(${`${userInfo.face}`.replace(
|
||||
'http:',
|
||||
'',
|
||||
)})`,
|
||||
}"
|
||||
rounded-full w-40px h-40px
|
||||
shadow="$bew-shadow-2"
|
||||
bg="$bew-fill-3 cover center"
|
||||
z-1
|
||||
/>
|
||||
<div
|
||||
ref="avatarShadow"
|
||||
class="avatar-shadow"
|
||||
:class="{ hover: popupVisible.userPanel }"
|
||||
:style="{
|
||||
backgroundImage: `url(${`${userInfo.face}`.replace(
|
||||
'http:',
|
||||
'',
|
||||
)})`,
|
||||
}"
|
||||
pos="absolute top-0" z-0 pointer-events-none
|
||||
bg="cover center" blur-sm
|
||||
rounded-full
|
||||
w-40px h-40px
|
||||
/>
|
||||
<svg
|
||||
v-if="userInfo.vip?.status === 1"
|
||||
class="vip-img"
|
||||
:class="{ hover: popupVisible.userPanel }"
|
||||
:style="{ opacity: popupVisible.userPanel ? 1 : 0 }"
|
||||
bg="[url(https://i0.hdslb.com/bfs/seed/jinkela/short/user-avatar/big-vip.svg)] cover no-repeat"
|
||||
w="27.5%" h="27.5%" z-1
|
||||
pos="absolute bottom-0 right-0" duration-300
|
||||
/>
|
||||
<Transition name="slide-in">
|
||||
<OldUserPanelPop
|
||||
v-if="popupVisible.userPanel"
|
||||
:user-info="userInfo"
|
||||
after:h="!0"
|
||||
class="bew-popover"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="others"
|
||||
:class="{ inactive: rightSideInactive }"
|
||||
style="
|
||||
backdrop-filter: var(--bew-filter-glass-1);
|
||||
box-shadow: var(--bew-shadow-edge-glow-1), var(--bew-shadow-2);
|
||||
"
|
||||
flex h-50px px-6px bg="$bew-elevated"
|
||||
transition="transition-property-colors duration-150"
|
||||
text="$bew-text-1" border="1 $bew-border-color" rounded-full
|
||||
transform-gpu
|
||||
>
|
||||
<div v-if="!isLogin" class="right-side-item">
|
||||
<a href="https://passport.bilibili.com/login" class="login">
|
||||
<div i-ic:outline-account-circle class="text-xl mr-2" />{{
|
||||
$t('topbar.sign_in')
|
||||
}}
|
||||
</a>
|
||||
</div>
|
||||
<template v-if="isLogin">
|
||||
<!-- TODO: need to refactor to this -->
|
||||
<!-- <div display="lg:flex none">
|
||||
<div v-for="item in topBarItems" :key="item.i18nKey" class="right-side-item">
|
||||
<a :href="item.url" target="_blank" :title="$t(item.i18nKey)">
|
||||
<Icon :icon="item.icon" />
|
||||
</a>
|
||||
|
||||
<Component
|
||||
:is="popups[item.popup] ?? 'div'"
|
||||
v-if="item.popup"
|
||||
class="bew-popover"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- TODO: need to refactor to above code -->
|
||||
<div class="hidden lg:flex" gap-1>
|
||||
<!-- Notifications -->
|
||||
<div
|
||||
ref="notifications"
|
||||
class="right-side-item"
|
||||
:class="{ active: popupVisible.notifications }"
|
||||
>
|
||||
<template v-if="unReadMessageCount > 0">
|
||||
<div
|
||||
v-if="settings.topBarIconBadges === 'number'"
|
||||
class="unread-num-dot"
|
||||
>
|
||||
{{ unReadMessageCount > 99 ? '99+' : unReadMessageCount }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="settings.topBarIconBadges === 'dot'"
|
||||
class="unread-dot"
|
||||
/>
|
||||
</template>
|
||||
<a
|
||||
href="https://message.bilibili.com"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="$t('topbar.notifications')"
|
||||
>
|
||||
<div i-tabler:bell />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<NotificationsPop
|
||||
v-if="popupVisible.notifications"
|
||||
class="bew-popover"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Moments -->
|
||||
<div
|
||||
ref="moments"
|
||||
class="right-side-item"
|
||||
:class="{ active: popupVisible.moments }"
|
||||
>
|
||||
<template v-if="newMomentsCount > 0">
|
||||
<div
|
||||
v-if="settings.topBarIconBadges === 'number'"
|
||||
class="unread-num-dot"
|
||||
>
|
||||
{{ newMomentsCount > 99 ? '99+' : newMomentsCount }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="settings.topBarIconBadges === 'dot'"
|
||||
class="unread-dot"
|
||||
/>
|
||||
</template>
|
||||
<a
|
||||
href="https://t.bilibili.com"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="$t('topbar.moments')"
|
||||
>
|
||||
<div i-tabler:windmill />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<MomentsPop v-show="popupVisible.moments" ref="momentsPopRef" class="bew-popover" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Favorites -->
|
||||
<div
|
||||
ref="favorites"
|
||||
class="right-side-item"
|
||||
:class="{ active: popupVisible.favorites }"
|
||||
>
|
||||
<a
|
||||
:href="`https://space.bilibili.com/${mid}/favlist`"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="$t('topbar.favorites')"
|
||||
>
|
||||
<div i-mingcute:star-line />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<KeepAlive>
|
||||
<FavoritesPop
|
||||
v-if="popupVisible.favorites"
|
||||
ref="favoritesPopRef"
|
||||
class="bew-popover"
|
||||
/>
|
||||
</KeepAlive>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- History -->
|
||||
<div
|
||||
ref="history"
|
||||
class="right-side-item"
|
||||
:class="{ active: popupVisible.history }"
|
||||
>
|
||||
<a
|
||||
href="https://www.bilibili.com/account/history"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="$t('topbar.history')"
|
||||
>
|
||||
<div i-mingcute:time-line />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<HistoryPop v-if="popupVisible.history" class="bew-popover" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Watch later -->
|
||||
<div
|
||||
ref="watchLater"
|
||||
class="right-side-item"
|
||||
:class="{ active: popupVisible.watchLater }"
|
||||
>
|
||||
<a
|
||||
href="https://www.bilibili.com/watchlater/#/list"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="$t('topbar.watch_later')"
|
||||
>
|
||||
<div i-mingcute:carplay-line />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<WatchLaterPop v-if="popupVisible.watchLater" class="bew-popover" ml--30px />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Creative center -->
|
||||
<div class="right-side-item">
|
||||
<a
|
||||
href="https://member.bilibili.com/platform/home"
|
||||
target="_blank"
|
||||
:title="$t('topbar.creative_center')"
|
||||
>
|
||||
<div i-mingcute:bulb-line />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- More -->
|
||||
<div
|
||||
ref="more"
|
||||
class="right-side-item lg:!hidden flex"
|
||||
:class="{ active: popupVisible.more }"
|
||||
>
|
||||
<a title="More">
|
||||
<div i-mingcute:menu-line />
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<MorePop v-show="popupVisible.more" class="bew-popover" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Upload -->
|
||||
<div
|
||||
ref="upload"
|
||||
class="upload right-side-item"
|
||||
>
|
||||
<a
|
||||
href="https://member.bilibili.com/platform/upload/video/frame"
|
||||
target="_blank"
|
||||
:title="$t('topbar.upload')"
|
||||
bg="$bew-theme-color"
|
||||
rounded-40px
|
||||
un-text="!white !base"
|
||||
w-35px h-35px ml-1
|
||||
flex="~ justify-center"
|
||||
shadow
|
||||
filter="hover:brightness-110"
|
||||
style="--un-shadow: 0 0 10px var(--bew-theme-color-60)"
|
||||
>
|
||||
<div i-mingcute:upload-2-line flex-shrink-0 />
|
||||
<!-- <span m="l-2" class="hidden xl:block">{{
|
||||
$t('topbar.upload')
|
||||
}}</span> -->
|
||||
</a>
|
||||
|
||||
<Transition name="slide-in">
|
||||
<UploadPop
|
||||
v-if="popupVisible.upload"
|
||||
class="bew-popover"
|
||||
pos="!left-auto !right-0"
|
||||
transform="!translate-x-0"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</header>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-bar-enter-active,
|
||||
.top-bar-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.top-bar-enter-from,
|
||||
.top-bar-leave-to {
|
||||
--uno: "opacity-0 transform -translate-y-full";
|
||||
}
|
||||
|
||||
.slide-in-enter-active,
|
||||
.slide-in-leave-active {
|
||||
--uno: "transition-all duration-300 pointer-events-none transform-gpu";
|
||||
}
|
||||
|
||||
.slide-in-leave-to,
|
||||
.slide-in-enter-from {
|
||||
--uno: "transform important: translate-y-4 opacity-0";
|
||||
}
|
||||
|
||||
.slide-out-enter-active,
|
||||
.slide-out-leave-active {
|
||||
--uno: "transition-all duration-300 pointer-events-none transform-gpu";
|
||||
}
|
||||
|
||||
.slide-out-leave-to,
|
||||
.slide-out-enter-from {
|
||||
--uno: "transform important: -translate-y-4 opacity-0";
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
--uno: "transition-all duration-600";
|
||||
}
|
||||
|
||||
.fade-leave-to,
|
||||
.fade-enter-from {
|
||||
--uno: "opacity-0";
|
||||
}
|
||||
|
||||
.hide {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.bew-popover {
|
||||
--uno: "absolute top-60px left-1/2";
|
||||
--uno: "transform -translate-x-1/2";
|
||||
--uno: "overflow-visible";
|
||||
--uno: "after:content-empty";
|
||||
--uno: "after:opacity-100 after:w-full after:h-100px";
|
||||
--uno: "after:absolute after:top--30px after:left-1/2 after:-z-1";
|
||||
--uno: "after:transform after:-translate-x-1/2";
|
||||
}
|
||||
|
||||
.logo.activated {
|
||||
--uno: "bg-$bew-theme-color dark:bg-white";
|
||||
|
||||
svg {
|
||||
--uno: "fill-white dark:fill-$bew-theme-color";
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
.avatar {
|
||||
--uno: "flex items-center mr-4 relative z-1";
|
||||
|
||||
.avatar-img,
|
||||
.avatar-shadow {
|
||||
--uno: "duration-300";
|
||||
|
||||
&.hover {
|
||||
--uno: "transform scale-230 translate-y-36px";
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-shadow {
|
||||
--uno: "opacity-0";
|
||||
|
||||
&.hover {
|
||||
--uno: "opacity-60";
|
||||
}
|
||||
}
|
||||
|
||||
.vip-img {
|
||||
&.hover {
|
||||
--uno: "transform scale-180 translate-y-55px translate-15px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.others.inactive,
|
||||
.others:hover {
|
||||
--uno: "important-backdrop-filter-none important-bg-$bew-elevated-solid";
|
||||
}
|
||||
|
||||
.unread-num-dot {
|
||||
--uno: "absolute top-4px right--4px";
|
||||
--uno: "important:px-1 rounded-full";
|
||||
--uno: "text-xs leading-0 z-6 min-w-16px h-16px";
|
||||
--uno: "grid place-items-center";
|
||||
--uno: "bg-$bew-theme-color text-white";
|
||||
box-shadow: 0 2px 4px rgba(var(--tw-shadow-color), 0.4);
|
||||
}
|
||||
|
||||
.unread-dot {
|
||||
--uno: "w-8px h-8px bg-$bew-theme-color rounded-8px absolute right-0 top-6px";
|
||||
}
|
||||
|
||||
.right-side-item {
|
||||
--uno: "relative text-$bew-text-1 flex items-center";
|
||||
|
||||
&:not(.avatar) a {
|
||||
--uno: "text-xl flex items-center p-2 rounded-40px duration-300 relative z-5";
|
||||
--uno: "h-35px h-35px";
|
||||
}
|
||||
|
||||
&.active a,
|
||||
&:not(.upload) a:hover {
|
||||
--un-drop-shadow: drop-shadow(0 0 6px white);
|
||||
--uno: "bg-$bew-fill-2 shadow-[var(--bew-shadow-edge-glow-1),var(--bew-shadow-1)]";
|
||||
}
|
||||
}
|
||||
|
||||
.right-side-item .login {
|
||||
--un-drop-shadow: drop-shadow(0 0 6px var(--bew-theme-color));
|
||||
--uno: "rounded-full mx-1 important:text-$bew-theme-color important:px-4 hover:important-bg-$bew-theme-color hover:important-text-white flex items-center justify-center important:text-base w-120px border-solid border-$bew-theme-color border-2 important:dark:filter";
|
||||
}
|
||||
}
|
||||
</style>
|
||||
322
src/components/TopBar/oldTopBarComponents/OldUserPanelPop.vue
Normal file
322
src/components/TopBar/oldTopBarComponents/OldUserPanelPop.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<script setup lang="ts">
|
||||
import DOMPurify from 'dompurify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useApiClient } from '~/composables/api'
|
||||
import { revokeAccessKey } from '~/utils/authProvider'
|
||||
import { numFormatter } from '~/utils/dataFormatter'
|
||||
import { LV0_ICON, LV1_ICON, LV2_ICON, LV3_ICON, LV4_ICON, LV5_ICON, LV6_ICON, LV6_LIGHTNING_ICON } from '~/utils/lvIcons'
|
||||
import { getCSRF, getUserID, isHomePage } from '~/utils/main'
|
||||
|
||||
import type { UserInfo, UserStat } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
userInfo: UserInfo
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const api = useApiClient()
|
||||
|
||||
const mid = computed(() => {
|
||||
return getUserID()
|
||||
})
|
||||
|
||||
const otherLinks = computed((): { name: string, url: string, icon: string }[] => {
|
||||
return [
|
||||
{
|
||||
name: t('topbar.user_dropdown.uploads_manager'),
|
||||
url: 'https://member.bilibili.com/v2#/upload-manager/article',
|
||||
icon: 'i-solar:video-library-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.account_settings'),
|
||||
url: 'https://account.bilibili.com/account/home',
|
||||
icon: 'i-solar:user-circle-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.b_coins_wallet'),
|
||||
url: 'https://pay.bilibili.com/',
|
||||
icon: 'i-solar:wallet-money-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.orders'),
|
||||
url: 'https://show.bilibili.com/orderlist',
|
||||
icon: 'i-solar:clipboard-list-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.workshop'),
|
||||
url: 'https://gf.bilibili.com?msource=main_station',
|
||||
icon: 'i-solar:garage-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.my_stream_info'),
|
||||
url: 'https://link.bilibili.com/p/center/index',
|
||||
icon: 'i-solar:videocamera-record-bold-duotone',
|
||||
},
|
||||
{
|
||||
name: t('topbar.user_dropdown.my_courses'),
|
||||
url: 'https://www.bilibili.com/cheese/mine/list',
|
||||
icon: 'i-solar:notebook-bookmark-bold-duotone',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const levelProgressBarWidth = computed(() => {
|
||||
const { next_exp: nextExp, current_exp: currentExp, current_min: minExp } = props.userInfo.level_info
|
||||
|
||||
const totalExp = nextExp - minExp
|
||||
const earnedExp = currentExp - minExp
|
||||
|
||||
if (totalExp === 0)
|
||||
return '0%'
|
||||
|
||||
const percentage = (earnedExp / totalExp) * 100
|
||||
return `${percentage.toFixed(2)}%`
|
||||
})
|
||||
|
||||
const userStat = reactive<UserStat>({} as UserStat)
|
||||
|
||||
onMounted(() => {
|
||||
api.user.getUserStat()
|
||||
.then((res) => {
|
||||
if (res.code === 0)
|
||||
Object.assign(userStat, res.data)
|
||||
})
|
||||
})
|
||||
|
||||
async function logout() {
|
||||
revokeAccessKey()
|
||||
api.auth.logout({
|
||||
biliCSRF: getCSRF(),
|
||||
}).then(() => {
|
||||
location.reload()
|
||||
})
|
||||
}
|
||||
|
||||
const levelIcons: string[] = [
|
||||
LV0_ICON,
|
||||
LV1_ICON,
|
||||
LV2_ICON,
|
||||
LV3_ICON,
|
||||
LV4_ICON,
|
||||
LV5_ICON,
|
||||
LV6_ICON,
|
||||
LV6_LIGHTNING_ICON,
|
||||
]
|
||||
|
||||
function getLvIcon(level: number, isSigma: boolean = false): string {
|
||||
if (level === 6 && isSigma) {
|
||||
return LV6_LIGHTNING_ICON
|
||||
}
|
||||
return levelIcons[level] || ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
style="backdrop-filter: var(--bew-filter-glass-1);"
|
||||
p-4 rounded="$bew-radius" w-300px z--1 bg="$bew-elevated-alt"
|
||||
border="1 $bew-border-color"
|
||||
shadow="[var(--bew-shadow-3),var(--bew-shadow-edge-glow-1)]"
|
||||
>
|
||||
<div
|
||||
text="xl" flex="~ items-center justify-center"
|
||||
mt-8 font-medium
|
||||
>
|
||||
{{ userInfo.uname ? userInfo.uname : '-' }}
|
||||
</div>
|
||||
<div
|
||||
text="xs $bew-text-2"
|
||||
flex="~ items-center justify-center"
|
||||
m="t-1 b-2"
|
||||
>
|
||||
<a
|
||||
class="group mr-4"
|
||||
href="https://account.bilibili.com/account/coin"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
>{{ $t('topbar.user_dropdown.money') + (userInfo.money ?? '-') }}</a>
|
||||
<a
|
||||
class="group"
|
||||
href="https://pay.bilibili.com/pay-v2-web/bcoin_index"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
>{{
|
||||
$t('topbar.user_dropdown.b_coins') + (userInfo.wallet?.bcoin_balance ?? '-')
|
||||
}}</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="//account.bilibili.com/account/record?type=exp"
|
||||
target="_blank"
|
||||
block mb-2 w-full
|
||||
flex="~ col justify-center items-center"
|
||||
>
|
||||
<template v-if="userInfo?.level_info?.current_level < 6">
|
||||
<div
|
||||
flex="~ items-center justify-center gap-2"
|
||||
w-full
|
||||
>
|
||||
<div
|
||||
flex="~ items-center"
|
||||
class="level"
|
||||
v-html="DOMPurify.sanitize(getLvIcon(userInfo.level_info.current_level))"
|
||||
/>
|
||||
<div relative w="full" h="2px" bg="$bew-fill-3">
|
||||
<div
|
||||
pos="absolute top-0 left-0" h-2px
|
||||
h="2px"
|
||||
rounded="2px"
|
||||
bg="$bew-warning-color"
|
||||
:style="{ width: levelProgressBarWidth }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="level level-next"
|
||||
flex="~ items-center"
|
||||
v-html="DOMPurify.sanitize(getLvIcon(userInfo.level_info.current_level + 1))"
|
||||
/>
|
||||
</div>
|
||||
<div w-full text="xs $bew-text-3">
|
||||
<!-- Current XP: 103; need 500 more for LV2. -->
|
||||
{{
|
||||
$t('topbar.user_dropdown.exp_desc', {
|
||||
current_exp: userInfo.level_info.current_exp,
|
||||
level: userInfo.level_info.current_level + 1,
|
||||
need_exp: userInfo.level_info.next_exp - userInfo.level_info.current_exp || 0,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
:style="{ width: userInfo?.is_senior_member ? '36px' : '28px' }"
|
||||
h-20px
|
||||
flex="~ items-center"
|
||||
v-html="DOMPurify.sanitize(getLvIcon(userInfo?.level_info?.current_level, userInfo?.is_senior_member))"
|
||||
/>
|
||||
</template>
|
||||
</a>
|
||||
|
||||
<div grid="~ cols-3 gap-2" mb-2>
|
||||
<a
|
||||
class="channel-info-item"
|
||||
:href="`https://space.bilibili.com/${mid}/fans/follow`"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="`${userStat.following}`"
|
||||
>
|
||||
<div class="num">
|
||||
{{ userStat.following ? numFormatter(userStat.following) : '0' }}
|
||||
</div>
|
||||
<div>{{ $t('topbar.user_dropdown.following') }}</div>
|
||||
</a>
|
||||
<a
|
||||
class="channel-info-item"
|
||||
:href="`https://space.bilibili.com/${mid}/fans/fans`"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="`${userStat.follower}`"
|
||||
>
|
||||
<div class="num">
|
||||
{{ userStat.follower ? numFormatter(userStat.follower) : '0' }}
|
||||
</div>
|
||||
<div>{{ $t('topbar.user_dropdown.followers') }}</div>
|
||||
</a>
|
||||
<a
|
||||
class="channel-info-item"
|
||||
:href="`https://space.bilibili.com/${mid}/dynamic`"
|
||||
:target="isHomePage() ? '_blank' : '_self'"
|
||||
:title="`${userStat.dynamic_count}`"
|
||||
>
|
||||
<div class="num">
|
||||
{{
|
||||
userStat.dynamic_count ? numFormatter(userStat.dynamic_count) : '0'
|
||||
}}
|
||||
</div>
|
||||
<div>{{ $t('topbar.user_dropdown.posts') }}</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ justify-between col gap-1"
|
||||
mb-2 p-2 bg="$bew-fill-alt" rounded="$bew-radius"
|
||||
shadow="[var(--bew-shadow-edge-glow-1),var(--bew-shadow-1)]"
|
||||
>
|
||||
<a
|
||||
v-for="item in otherLinks.filter((_, index) => index <= 1)"
|
||||
:key="item.url"
|
||||
:href="item.url"
|
||||
target="_blank"
|
||||
p="x-4 y-2" flex="~ items-center justify-between"
|
||||
rounded="$bew-radius"
|
||||
duration-300
|
||||
bg="hover:$bew-fill-2"
|
||||
>
|
||||
<div flex="~ items-center gap-2">
|
||||
<div :class="item.icon" text="$bew-text-2" />
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div i-mingcute:arrow-right-line />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ justify-between col gap-1"
|
||||
p-2 bg="$bew-fill-alt" rounded="$bew-radius"
|
||||
shadow="[var(--bew-shadow-edge-glow-1),var(--bew-shadow-1)]"
|
||||
>
|
||||
<a
|
||||
v-for="item in otherLinks.filter((_, index) => index > 1)"
|
||||
:key="item.url"
|
||||
:href="item.url"
|
||||
target="_blank"
|
||||
p="x-4 y-2" flex="~ items-center justify-between"
|
||||
rounded="$bew-radius"
|
||||
duration-300
|
||||
hover:bg="$bew-fill-2"
|
||||
>
|
||||
<div flex="~ items-center gap-2">
|
||||
<div :class="item.icon" text="$bew-text-2" />
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div i-mingcute:arrow-right-line />
|
||||
</a>
|
||||
<div
|
||||
text="$bew-error-color"
|
||||
p="x-4 y-2" flex="~ items-center"
|
||||
rounded="$bew-radius"
|
||||
duration-300 cursor-pointer
|
||||
hover:bg="$bew-fill-2"
|
||||
@click="logout()"
|
||||
>
|
||||
<div i-solar:logout-2-bold-duotone text="$bew-error-60" mr-2 />
|
||||
{{ $t('topbar.user_dropdown.log_out') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.level :deep(svg) {
|
||||
--uno: "w-25px h-16px";
|
||||
}
|
||||
|
||||
.level-next :deep(svg .level-bg) {
|
||||
--uno: "fill-#c9ccd0";
|
||||
}
|
||||
|
||||
.channel-info-item {
|
||||
--uno: "p-2 m-0 rounded-$bew-radius text-sm flex flex-col items-center transition-all duration-300";
|
||||
--uno: "bg-$bew-fill-alt hover:bg-$bew-fill-2";
|
||||
--uno: "shadow-[var(--bew-shadow-edge-glow-1),var(--bew-shadow-1)]";
|
||||
|
||||
> * {
|
||||
--uno: "transition-all duration-300";
|
||||
}
|
||||
|
||||
.num {
|
||||
--uno: "font-semibold text-xl";
|
||||
|
||||
+ div {
|
||||
--uno: "text-$bew-text-2 mt-1 text-xs font-semibold";
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -10,6 +10,7 @@ import Dock from '~/components/Dock/Dock.vue'
|
||||
import OverlayScrollbarsComponent from '~/components/OverlayScrollbarsComponent'
|
||||
import RightSideButtons from '~/components/RightSideButtons/RightSideButtons.vue'
|
||||
import Settings from '~/components/Settings/Settings.vue'
|
||||
import OldTopBar from '~/components/TopBar/OldTopBar.vue'
|
||||
import TopBar from '~/components/TopBar/TopBar.vue'
|
||||
import type { BewlyAppProvider } from '~/composables/useAppProvider'
|
||||
import { useDark } from '~/composables/useDark'
|
||||
@@ -303,7 +304,12 @@ provide<BewlyAppProvider>('BEWLY_APP', {
|
||||
|
||||
<!-- TopBar -->
|
||||
<div m-auto max-w="$bew-page-max-width">
|
||||
<OldTopBar
|
||||
v-if="settings.useOldTopBar"
|
||||
pos="top-0 left-0" z="99 hover:1001" w-full
|
||||
/>
|
||||
<TopBar
|
||||
v-else
|
||||
pos="top-0 left-0" z="99 hover:1001" w-full
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface Settings {
|
||||
enableVideoPreview: boolean
|
||||
enableVideoCtrlBarOnVideoCard: boolean
|
||||
hoverVideoCardDelayed: boolean
|
||||
useOldTopBar: boolean
|
||||
autoHideTopBar: boolean
|
||||
topBarIconBadges: 'number' | 'dot' | 'none'
|
||||
blockAds: boolean
|
||||
@@ -72,6 +73,7 @@ export const settings = useStorageLocal('settings', ref<Settings>({
|
||||
enableVideoPreview: true,
|
||||
enableVideoCtrlBarOnVideoCard: false,
|
||||
hoverVideoCardDelayed: false,
|
||||
useOldTopBar: false,
|
||||
autoHideTopBar: false,
|
||||
topBarIconBadges: 'number',
|
||||
dockPosition: 'right',
|
||||
|
||||
Reference in New Issue
Block a user