Skip to content

Commit

Permalink
admin: Add login and logout
Browse files Browse the repository at this point in the history
  • Loading branch information
ThrRip committed Dec 5, 2023
1 parent 72bab37 commit 181d056
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 6 deletions.
139 changes: 133 additions & 6 deletions packages/admin/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
class="grid grid-rows-[auto_1fr_auto] max-lg:landscape:gap-y-2 gap-y-4 self-center
px-12 max-lg:landscape:py-2 py-7 w-screen sm:w-[26rem] h-2/3 lg:h-[calc(100dvh_-_14rem)]
text-white transition-[margin-left] duration-300"
:class="{ '-ml-[16.5rem]': !viewShowFullNavigationBar, 'z-50': viewShowFullNavigationBar }"
:class="{
'-ml-[26rem]': !backendLoggedIn,
'-ml-[16.5rem]': !viewShowFullNavigationBar,
'z-50': viewShowFullNavigationBar
}"
>
<section class="z-50 flex flex-row max-lg:landscape:gap-y-2 gap-y-4 justify-between items-center">
<h1 class="min-w-max text-xl sm:text-2xl text-white-alt transition-opacity" :class="{ 'opacity-0': !viewShowFullNavigationBar }">
Expand Down Expand Up @@ -46,7 +50,62 @@
</span>
</NuxtLink>
</section>
<section class="z-50 flex flex-row justify-end">
<section class="z-50 flex flex-row gap-x-12 justify-between">
<button
class="flex flex-row gap-x-1 items-center px-2 w-full h-14
bg-blue hover:bg-blue-a focus:outline outline-2 outline-offset-3 outline-blue-a rounded-xl
transition duration-300 active:scale-95"
@click="backendLogout"
>
<ClientOnly>
<transition-group
tag="span"
class="aspect-square grid grid-areas-stack place-items-center pl-0.5 h-14"
enter-from-class="opacity-0"
enter-active-class="transition-opacity duration-200"
leave-active-class="transition-opacity duration-200"
leave-to-class="opacity-0"
>
<font-awesome-icon
v-if="backendAuthState === ''"
key="idle"
:icon="['fas', 'right-from-bracket']"
class="max-lg:landscape:!h-5 !h-6"
/>
<font-awesome-icon
v-if="backendAuthState === 'processing'"
key="processing"
:icon="['fas', 'circle-notch']"
spin
class="max-lg:landscape:!h-5 !h-6"
/>
<font-awesome-icon
v-if="backendAuthState === 'succeeded'"
key="succeeded"
:icon="['fas', 'check']"
class="max-lg:landscape:!h-5 !h-6"
/>
<font-awesome-icon
v-if="backendAuthState === 'failed'"
key="failed"
:icon="['fas', 'circle-exclamation']"
class="max-lg:landscape:!h-5 !h-6"
/>
</transition-group>
</ClientOnly>
<transition-group
tag="span"
class="grid grid-areas-stack justify-items-start"
enter-from-class="opacity-0"
enter-active-class="transition-opacity duration-200"
leave-active-class="transition-opacity duration-200"
leave-to-class="opacity-0"
>
<span v-if="backendAuthState === 'succeeded'" key="succeeded">已退出登录</span>
<span v-else-if="backendAuthState === 'failed'" key="failed">请重试</span>
<span v-else key="idle">退出登录</span>
</transition-group>
</button>
<button
:title="viewShowFullNavigationBar ? '收起导航栏' : '展开导航栏'"
class="aspect-square flex flex-row justify-center items-center h-14
Expand All @@ -66,10 +125,17 @@
</button>
</section>
</nav>
<main class="overflow-x-clip overflow-y-auto lg:overflow-y-hidden grid grid-cols-[1fr] grid-rows-[1fr] h-full bg-white-alt">
<main class="overflow-x-clip overflow-y-auto lg:overflow-y-hidden grid grid-cols-[1fr] grid-rows-[1fr] h-full">
<LoginModal
v-if="!backendLoggedInChecking && !backendLoggedIn"
:visible="viewShowLoginModal"
:auth-state="backendAuthState"
@submit="backendLogin"
/>
<NuxtPage
v-if="backendLoggedIn"
:backend-client="backendClient"
class="z-20 transition-opacity duration-300"
class="transition-opacity duration-300"
:class="{
'opacity-0': !viewShowPageContent,
'hidden': viewShowFullNavigationBar && viewShowFullNavigationBarRealState
Expand All @@ -80,7 +146,10 @@
<div
class="-z-20 grid grid-areas-stack justify-items-center items-center
-ml-[13.5rem] sm:-ml-[13rem] w-[52rem] h-full transition-[margin-left] duration-300"
:class="{ '!-ml-[30rem] sm:!-ml-[29rem]': !viewShowFullNavigationBar }"
:class="{
'!-ml-[52rem] sm:!-ml-[52rem]': !backendLoggedIn,
'!-ml-[30rem] sm:!-ml-[29rem]': !viewShowFullNavigationBar
}"
>
<div class="w-[85vw] sm:w-1/2 h-2/3 lg:h-[calc(100dvh_-_14rem)] bg-blue-l" />
</div>
Expand All @@ -89,7 +158,7 @@
</template>

<script setup lang="ts">
import { Client } from 'appwrite'
import { Account, Client } from 'appwrite'
const route = useRoute()
Expand All @@ -103,8 +172,66 @@ if (route.query.entrytoken) {
const backendClient = new Client()
backendClient.setEndpoint(useAppConfig().backendBase)
.setProject(useAppConfig().backendProjectId)
const backendAccount = new Account(backendClient)
const backendLoggedIn = ref<Boolean>(false)
const backendLoggedInChecking = ref<Boolean>(true)
onBeforeMount(() => {
backendAccount.get()
.then(
() => {
backendLoggedIn.value = true
backendLoggedInChecking.value = false
},
async () => {
await navigateTo('/')
backendLoggedInChecking.value = false
viewShowLoginModal.value = true
}
)
})
export type BackendAuthState = '' | 'processing' | 'succeeded' | 'failed'
const backendAuthState = ref<BackendAuthState>('')
function backendLogin (email: string, password: string) {
backendAuthState.value = 'processing'
backendAccount.createEmailSession(email, password)
.then(
() => {
backendAuthState.value = 'succeeded'
setTimeout(() => {
viewShowLoginModal.value = false
}, 1000)
setTimeout(() => {
backendAuthState.value = ''
backendLoggedIn.value = true
}, 1300)
},
() => { backendAuthState.value = 'failed' }
)
}
function backendLogout () {
backendAuthState.value = 'processing'
backendAccount.deleteSession('current')
.then(
() => {
backendAuthState.value = 'succeeded'
setTimeout(() => {
viewShowFullNavigationBar.value = false
backendAuthState.value = ''
backendLoggedIn.value = false
nextTick(() => { viewShowLoginModal.value = true })
}, 1000)
},
() => { backendAuthState.value = 'failed' }
)
}
// View
const viewShowLoginModal = ref<Boolean>(false)
const viewShowFullNavigationBar = ref<Boolean>(false)
const viewShowFullNavigationBarToggles = ref<number>(0)
const viewShowFullNavigationBarRealState = ref<Boolean>(false)
Expand Down
82 changes: 82 additions & 0 deletions packages/admin/components/LoginModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<div class="grid grid-areas-stack place-items-center">
<div
class="flex flex-col gap-y-8 justify-center items-center w-4/5 max-w-xs transition-opacity duration-300"
:class="{ 'opacity-0': !props.visible }"
>
<div class="flex flex-col gap-y-1 text-white">
登录至
<h1 class="text-3xl">
管理面板|洺知-故犯
</h1>
</div>
<form class="flex flex-col w-full">
<input
v-model="email"
type="email"
:readonly="props.authState === 'processing'"
class="px-5 h-14 text-black bg-white-alta/75 focus:bg-white-alta/95
rounded-t-xl focus:outline-none backdrop-blur transition-colors duration-500"
placeholder="邮箱"
@keydown.enter="submit"
>
<input
v-model="password"
type="password"
:readonly="props.authState === 'processing'"
class="px-5 h-14 text-black bg-white-alta/75 focus:bg-white-alta/95
rounded-b-xl focus:outline-none backdrop-blur transition-colors duration-500"
placeholder="密码"
@keydown.enter="submit"
>
</form>
<button
class="grid grid-areas-stack place-items-center w-full h-12
text-white bg-blue hover:bg-blue-a rounded-xl focus:outline outline-2 outline-offset-3 outline-blue-a
transition duration-200 active:scale-95"
@click="submit"
>
<transition-group
enter-from-class="opacity-0"
enter-active-class="transition-opacity duration-200"
leave-active-class="transition-opacity duration-200"
leave-to-class="opacity-0"
>
<span v-if="props.authState === ''" key="idle">登录</span>
<font-awesome-icon v-if="props.authState === 'processing'" key="processing" :icon="['fas', 'circle-notch']" spin class="!h-5" />
<font-awesome-icon v-if="props.authState === 'succeeded'" key="succeeded" :icon="['fas', 'check']" class="!h-5" />
<span v-if="props.authState === 'failed'" key="failed" class="flex flex-row gap-x-2 items-center">
<font-awesome-icon :icon="['fas', 'circle-exclamation']" class="!h-5" />
请重试
</span>
</transition-group>
</button>
</div>
<div
class="-z-20 w-full max-w-lg h-full max-h-[30rem] bg-blue-l transition-opacity duration-300"
:class="{ 'opacity-0': !props.visible }"
/>
</div>
</template>

<script setup lang="ts">
import type { BackendAuthState } from '../app.vue'
const props = defineProps<{
visible: boolean
authState: BackendAuthState
}>()
const emit = defineEmits<{
submit: [email: string, password: string]
}>()
const email = ref<string>('')
const password = ref<string>('')
function submit () {
if (props.authState === '' || props.authState === 'failed') {
emit('submit', email.value, password.value)
}
}
</script>
8 changes: 8 additions & 0 deletions packages/admin/plugins/fontawesome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { library, config } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

import {
faCircleNotch,
faCheck,
faCircleExclamation,
faHouse,
faPenToSquare,
faRightFromBracket,
faAngleRight,
faCaretUp,
faSquareMinus,
Expand All @@ -22,8 +26,12 @@ import {
config.autoAddCss = false

library.add(
faCircleNotch,
faCheck,
faCircleExclamation,
faHouse,
faPenToSquare,
faRightFromBracket,
faAngleRight,
faCaretUp,
faSquare,
Expand Down

0 comments on commit 181d056

Please sign in to comment.