Files
xsh-assistant-next/app/layouts/default.vue

290 lines
7.1 KiB
Vue

<script setup lang="ts">
import ModalAuthentication from '~/components/ModalAuthentication.vue'
const colorMode = useColorMode()
const dayjs = useDayjs()
const modal = useModal()
const toast = useToast()
const router = useRouter()
const loginState = useLoginState()
const isDark = computed({
get() {
return colorMode.value === 'dark'
},
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
},
})
const links = [
{
label: '分身制课',
icon: 'tabler:books',
to: '/generation',
},
{
label: '聊天',
icon: 'tabler:message-chatbot',
to: '/aigc/chat',
},
{
label: '绘画',
icon: 'i-tabler-brush',
to: '/aigc/draw',
},
{
label: 'AI 工具导航',
icon: 'tabler:planet',
to: '/aigc/navigation',
},
]
const items = [
[
{
label: 'support@fenshenzhike.com',
slot: 'account',
disabled: true,
},
],
[
{
label: '账号管理',
icon: 'i-tabler-user-circle',
to: '/profile',
},
],
[
{
label: '注销登录',
icon: 'i-tabler-logout',
click: () =>
loginState.logout().then(() => {
toast.add({
title: '退出登录',
description: `您已成功退出登录账号`,
color: 'indigo',
icon: 'i-tabler-logout-2',
})
router.push({ path: '/user/authenticate' })
}),
},
],
]
const open_login_modal = () => {
modal.open(ModalAuthentication)
}
onMounted(async () => {
if (loginState.user.auth_code === 2) {
/** Adminstrator notifications */
// Fetch unverified users
const unverifiedUsers = await useFetchWrapped<
req.user.UserList & AuthedRequest,
BaseResponse<PagedData<UserSchema>>
>('App.User_User.ListUser', {
token: loginState.token!,
user_id: loginState.user.id!,
page: 1,
perpage: 20,
is_verify: false,
})
if (!unverifiedUsers) {
toast.add({
title: '获取待审核用户失败',
description: '获取未审核用户列表失败',
color: 'red',
icon: 'i-tabler-alert-triangle',
})
} else if (unverifiedUsers.data.total > 0) {
toast.add({
title: '有新用户等待审核',
description: `${unverifiedUsers.data.total} 个新用户注册,请尽快审核`,
color: 'amber',
icon: 'i-tabler-user-plus',
timeout: 0,
actions: [
{
label: '前往处理',
variant: 'solid',
color: 'amber',
click: () => {
router.push({
path: '/generation/admin/users',
query: {
unverified: 1,
},
})
},
},
// {
// label: '全部忽略',
// click: () => {
// alert('ignored')
// },
// },
],
})
}
}
})
</script>
<template>
<div class="relative grid min-h-screen w-full">
<header>
<h1 class="inline-flex flex-col">
<span class="text-lg font-bold text-neutral-600 dark:text-neutral-300">
AIGC 微课视频研创平台
</span>
<!-- <span class="text-xs text-neutral-600 dark:text-neutral-300">眩生花科技</span> -->
</h1>
<!-- <div class="hidden md:block">
<UHorizontalNavigation
:links="links"
class="select-none"
/>
</div> -->
<div class="flex flex-row items-center gap-4">
<ClientOnly>
<UButton
:icon="
isDark
? 'i-line-md-sunny-outline-to-moon-alt-loop-transition'
: 'i-line-md-moon-alt-to-sunny-outline-loop-transition'
"
color="gray"
variant="ghost"
aria-label="Theme"
@click="isDark = !isDark"
/>
<UButton
v-if="!loginState.is_logged_in"
label="登录或注册"
size="xs"
class="font-bold"
color="indigo"
@click="open_login_modal"
/>
<UDropdown
v-if="loginState.is_logged_in"
:items="items"
:popper="{ placement: 'bottom-start' }"
:ui="{ item: { disabled: 'cursor-text select-text' } }"
>
<UAvatar
:alt="loginState.user.username.toUpperCase()"
:src="loginState.user.avatar"
chip-color="amber"
chip-position="bottom-right"
chip-text="OP"
size="md"
/>
<template #account="{ item }">
<div class="text-left">
<p class="flex items-center gap-1">
已登录为
<UBadge
v-if="loginState.user.auth_code === 2"
color="amber"
size="xs"
variant="subtle"
>
OP
</UBadge>
</p>
<p
class="max-w-40 truncate whitespace-nowrap font-medium text-gray-900 dark:text-white"
>
{{ loginState.user?.username }}
</p>
</div>
</template>
<template #item="{ item }">
<span class="truncate">{{ item.label }}</span>
<UIcon
:name="item.icon"
class="ms-auto size-4 flex-shrink-0 text-gray-400 dark:text-gray-500"
/>
</template>
</UDropdown>
</ClientOnly>
</div>
</header>
<main>
<slot />
</main>
<footer>
<p class="text-sm text-neutral-500">
© {{ dayjs().year() }} 重庆眩生花科技有限公司
</p>
</footer>
</div>
</template>
<style>
body {
@apply bg-neutral-50 bg-fixed dark:bg-neutral-950;
/* @apply bg-[url('~/assets/background-pattern.svg')] dark:bg-[url('~/assets/background-pattern-dark.svg')]; */
}
::-webkit-scrollbar {
--bar-width: 5px;
width: var(--bar-width);
height: var(--bar-width);
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
--bar-color: rgba(0, 0, 0, 0.1);
background-color: var(--bar-color);
border-radius: 20px;
background-clip: content-box;
border: 1px solid transparent;
}
/*
*::-webkit-scrollbar {
@apply w-1.5 h-1.5;
}
*::-webkit-scrollbar-track {
@apply bg-neutral-300/20 dark:bg-neutral-700/20;
}
*::-webkit-scrollbar-thumb {
@apply rounded;
@apply bg-neutral-300 dark:bg-neutral-700;
@apply hover:bg-neutral-400 hover:dark:bg-neutral-600;
}
*/
</style>
<style scoped>
header {
@apply fixed inset-x-0 z-30 h-16 border-b bg-white;
@apply dark:border-neutral-800 dark:bg-neutral-900;
@apply flex flex-row items-center justify-between px-4;
@apply bg-opacity-50 backdrop-blur-3xl backdrop-saturate-150 dark:bg-opacity-50;
}
main {
@apply h-screen pt-16;
}
footer {
@apply z-30 h-16 border-t bg-white;
@apply dark:border-neutral-800 dark:bg-neutral-900;
@apply flex flex-row items-center justify-between px-4;
}
</style>