RSS Git Download  Clone
Raw Blame History 6kB 164 lines
<script setup lang="ts">
import { ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18nStore } from '../../stores/i18n'
import { useAuthStore } from '../../stores/auth'
import { useThemeStore } from '../../stores/theme'

const i18n = useI18nStore()
const strings = computed(() => i18n.strings)
const auth = useAuthStore()
const themeStore = useThemeStore()
const { themeKey } = storeToRefs(themeStore)

const STRONG_BG: Record<string, string> = {
    enterprise: '#212121', light: '#263238', redis: '#b71c1c',
    dark: '#212121', darkNeu: '#263238', darkoBluo: '#1a237e',
    matrix: '#33691e',
}
const ACCORDION_BG: Record<string, string> = {
    enterprise: '#9e9e9e', light: '#b0bec5', redis: '#ef9a9a',
    dark: '#9e9e9e', darkNeu: '#90a4ae', darkoBluo: '#3f51b5',
    matrix: '#76ff03',
}
const headerBg = computed(() => STRONG_BG[themeKey.value])
const footerBg = computed(() => themeKey.value === 'matrix' ? '#0a2e0d' : (ACCORDION_BG[themeKey.value]))

const username = ref('')
const password = ref('')
const pwVisible = ref(false)
const loading = ref(false)
const guiMenu = ref(false)

const currentGui = (() => {
    try { return localStorage.getItem('p3xr-frontend') } catch { return 'ng' }
})()

async function handleLogin() {
    if (loading.value || !username.value || !password.value) return
    loading.value = true
    const success = await auth.login(username.value, password.value)
    if (success) location.reload()
    loading.value = false
}

// @ts-ignore
import { switchGui } from '../../../core/gui-switch'
</script>

<template>
    <div class="p3xr-login-wrapper">
        <div class="p3xr-login-card">
            <!-- Header -->
            <v-toolbar density="compact" :style="{ backgroundColor: headerBg, color: 'rgba(255,255,255,0.87)' }" class="p3xr-login-toolbar">
                <v-spacer />
                <v-menu v-model="guiMenu" location="bottom end">
                    <template #activator="{ props: mp }">
                        <v-btn v-bind="mp" variant="text" size="small" style="color: inherit;">
                            <v-icon size="18" class="mr-1">mdi-monitor</v-icon>
                            GUI
                        </v-btn>
                    </template>
                    <v-list density="compact">
                        <v-list-item :class="{ 'v-list-item--active': currentGui === 'ng' }" @click="switchGui('ng')">
                            <template #prepend><i class="fab fa-angular" style="font-size:18px;width:24px;text-align:center;margin-right:8px;color:#dd0031;"></i></template>
                            <v-list-item-title>Angular</v-list-item-title>
                        </v-list-item>
                        <v-list-item :class="{ 'v-list-item--active': currentGui === 'react' }" @click="switchGui('react')">
                            <template #prepend><i class="fab fa-react" style="font-size:18px;width:24px;text-align:center;margin-right:8px;color:#61dafb;"></i></template>
                            <v-list-item-title>React</v-list-item-title>
                        </v-list-item>
                        <v-list-item :class="{ 'v-list-item--active': currentGui === 'vue' }" @click="switchGui('vue')">
                            <template #prepend><i class="fab fa-vuejs" style="font-size:18px;width:24px;text-align:center;margin-right:8px;color:#42b883;"></i></template>
                            <v-list-item-title>Vue</v-list-item-title>
                        </v-list-item>
                    </v-list>
                </v-menu>
            </v-toolbar>

            <!-- Content -->
            <div class="p3xr-login-content">
                <v-text-field
                    v-model="username" autofocus
                    :label="str(strings?.form?.connection?.label?.username)"
                    autocomplete="username" density="comfortable" variant="outlined" hide-details class="mb-3"
                    @keydown.enter="handleLogin"
                >
                    <template #prepend-inner>
                        <v-icon size="20">mdi-account</v-icon>
                    </template>
                </v-text-field>
                <v-text-field
                    v-model="password"
                    :label="str(strings?.form?.connection?.label?.password)"
                    :type="pwVisible ? 'text' : 'password'"
                    autocomplete="current-password" density="comfortable" variant="outlined" hide-details class="mb-3"
                    @keydown.enter="handleLogin"
                >
                    <template #prepend-inner>
                        <v-icon size="20">mdi-lock</v-icon>
                    </template>
                    <template #append-inner>
                        <v-icon size="20" style="cursor:pointer;" @click="pwVisible = !pwVisible">
                            {{ pwVisible ? 'mdi-eye-off' : 'mdi-eye' }}
                        </v-icon>
                    </template>
                </v-text-field>
                <div v-if="auth.loginError" class="p3xr-login-error">
                    {{ strings?.confirm?.invalidCredentials }}
                </div>
            </div>

            <!-- Footer -->
            <div class="p3xr-login-footer" :style="{ backgroundColor: footerBg }">
                <v-btn color="primary" variant="flat" :disabled="loading || !username || !password" @click="handleLogin">
                    <v-icon size="20" class="mr-1">mdi-login</v-icon>
                    {{ strings?.intention?.ok }}
                </v-btn>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
function str(val: any, opts?: any): string {
    if (typeof val === 'function') return val(opts)
    return val ?? ''
}
</script>

<style scoped>
.p3xr-login-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: calc(100vh - 96px);
}
.p3xr-login-card {
    width: 400px;
    max-width: calc(100vw - 32px);
    border-radius: 4px;
    overflow: hidden;
    box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14), 0 9px 46px 8px rgba(0,0,0,.12);
}
.p3xr-login-toolbar {
    box-shadow: none !important;
}
.p3xr-login-content {
    padding: 16px;
    background-color: rgb(var(--v-theme-surface));
    color: rgb(var(--v-theme-on-surface));
}
.p3xr-login-footer {
    padding: 8px;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}
.p3xr-login-error {
    color: rgb(var(--v-theme-error));
    font-size: 13px;
    margin-top: 4px;
}
</style>