初始化
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export { default as LockScreenModal } from './lock-screen-modal.vue';
|
||||
export { default as LockScreen } from './lock-screen.vue';
|
||||
@@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@easyflow/types';
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
import { $t } from '@easyflow/locales';
|
||||
|
||||
import { useEasyFlowForm, z } from '@easyflow-core/form-ui';
|
||||
import { useEasyFlowModal } from '@easyflow-core/popup-ui';
|
||||
import { EasyFlowAvatar, EasyFlowButton } from '@easyflow-core/shadcn-ui';
|
||||
|
||||
interface Props {
|
||||
avatar?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'LockScreenModal',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
avatar: '',
|
||||
text: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [Recordable<any>];
|
||||
}>();
|
||||
|
||||
const [Form, { resetForm, validate, getValues, getFieldComponentRef }] =
|
||||
useEasyFlowForm(
|
||||
reactive({
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
hideRequiredMark: true,
|
||||
},
|
||||
schema: computed(() => [
|
||||
{
|
||||
component: 'EasyFlowInputPassword' as const,
|
||||
componentProps: {
|
||||
placeholder: $t('ui.widgets.lockScreen.placeholder'),
|
||||
},
|
||||
fieldName: 'lockScreenPassword',
|
||||
formFieldProps: { validateOnBlur: false },
|
||||
label: $t('authentication.password'),
|
||||
rules: z
|
||||
.string()
|
||||
.min(1, { message: $t('ui.widgets.lockScreen.placeholder') }),
|
||||
},
|
||||
]),
|
||||
showDefaultActions: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const [Modal] = useEasyFlowModal({
|
||||
onConfirm() {
|
||||
handleSubmit();
|
||||
},
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
resetForm();
|
||||
}
|
||||
},
|
||||
onOpened() {
|
||||
requestAnimationFrame(() => {
|
||||
getFieldComponentRef('lockScreenPassword')
|
||||
?.$el?.querySelector('[name="lockScreenPassword"]')
|
||||
?.focus();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
const { valid } = await validate();
|
||||
const values = await getValues();
|
||||
if (valid) {
|
||||
emit('submit', values?.lockScreenPassword);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
:footer="false"
|
||||
:fullscreen-button="false"
|
||||
:title="$t('ui.widgets.lockScreen.title')"
|
||||
>
|
||||
<div
|
||||
class="mb-10 flex w-full flex-col items-center px-10"
|
||||
@keydown.enter.prevent="handleSubmit"
|
||||
>
|
||||
<div class="w-full">
|
||||
<div class="ml-2 flex w-full flex-col items-center">
|
||||
<EasyFlowAvatar
|
||||
:src="avatar"
|
||||
class="size-20"
|
||||
dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
|
||||
/>
|
||||
<div class="text-foreground my-6 flex items-center font-medium">
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
<Form />
|
||||
<EasyFlowButton class="mt-1 w-full" @click="handleSubmit">
|
||||
{{ $t('ui.widgets.lockScreen.screenButton') }}
|
||||
</EasyFlowButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,167 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
import { LockKeyhole } from '@easyflow/icons';
|
||||
import { $t, useI18n } from '@easyflow/locales';
|
||||
import { storeToRefs, useAccessStore } from '@easyflow/stores';
|
||||
|
||||
import { useScrollLock } from '@easyflow-core/composables';
|
||||
import { useEasyFlowForm, z } from '@easyflow-core/form-ui';
|
||||
import { EasyFlowAvatar, EasyFlowButton } from '@easyflow-core/shadcn-ui';
|
||||
|
||||
import { useDateFormat, useNow } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'LockScreen',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
avatar: '',
|
||||
});
|
||||
|
||||
defineEmits<{ toLogin: [] }>();
|
||||
|
||||
const { locale } = useI18n();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const now = useNow();
|
||||
const meridiem = useDateFormat(now, 'A');
|
||||
const hour = useDateFormat(now, 'HH');
|
||||
const minute = useDateFormat(now, 'mm');
|
||||
const date = useDateFormat(now, 'YYYY-MM-DD dddd', { locales: locale.value });
|
||||
|
||||
const showUnlockForm = ref(false);
|
||||
const { lockScreenPassword } = storeToRefs(accessStore);
|
||||
|
||||
const [Form, { form, validate, getFieldComponentRef }] = useEasyFlowForm(
|
||||
reactive({
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
hideRequiredMark: true,
|
||||
},
|
||||
schema: computed(() => [
|
||||
{
|
||||
component: 'EasyFlowInputPassword' as const,
|
||||
componentProps: {
|
||||
placeholder: $t('ui.widgets.lockScreen.placeholder'),
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: $t('authentication.password'),
|
||||
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
||||
},
|
||||
]),
|
||||
showDefaultActions: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const validPass = computed(
|
||||
() => lockScreenPassword?.value === form?.values?.password,
|
||||
);
|
||||
|
||||
async function handleSubmit() {
|
||||
const { valid } = await validate();
|
||||
if (valid) {
|
||||
if (validPass.value) {
|
||||
accessStore.unlockScreen();
|
||||
} else {
|
||||
form.setFieldError('password', $t('authentication.passwordErrorTip'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleUnlockForm() {
|
||||
showUnlockForm.value = !showUnlockForm.value;
|
||||
if (showUnlockForm.value) {
|
||||
requestAnimationFrame(() => {
|
||||
getFieldComponentRef('password')
|
||||
?.$el?.querySelector('[name="password"]')
|
||||
?.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useScrollLock();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-background fixed z-[2000] size-full">
|
||||
<transition name="slide-left">
|
||||
<div v-show="!showUnlockForm" class="size-full">
|
||||
<div
|
||||
class="flex-col-center text-foreground/80 hover:text-foreground group fixed left-1/2 top-6 z-[2001] -translate-x-1/2 cursor-pointer text-xl font-semibold"
|
||||
@click="toggleUnlockForm"
|
||||
>
|
||||
<LockKeyhole
|
||||
class="size-5 transition-all duration-300 group-hover:scale-125"
|
||||
/>
|
||||
<span>{{ $t('ui.widgets.lockScreen.unlock') }}</span>
|
||||
</div>
|
||||
<div class="flex h-full w-full items-center justify-center">
|
||||
<div class="flex w-full justify-center gap-4 px-4 sm:gap-6 md:gap-8">
|
||||
<div
|
||||
class="bg-accent relative flex h-[140px] w-[140px] items-center justify-center rounded-xl text-[36px] sm:h-[160px] sm:w-[160px] sm:text-[42px] md:h-[200px] md:w-[200px] md:text-[72px]"
|
||||
>
|
||||
<span
|
||||
class="absolute left-3 top-3 text-xs font-semibold sm:text-sm md:text-xl"
|
||||
>
|
||||
{{ meridiem }}
|
||||
</span>
|
||||
{{ hour }}
|
||||
</div>
|
||||
<div
|
||||
class="bg-accent flex h-[140px] w-[140px] items-center justify-center rounded-xl text-[36px] sm:h-[160px] sm:w-[160px] sm:text-[42px] md:h-[200px] md:w-[200px] md:text-[72px]"
|
||||
>
|
||||
{{ minute }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<transition name="slide-right">
|
||||
<div
|
||||
v-if="showUnlockForm"
|
||||
class="flex-center size-full"
|
||||
@keydown.enter.prevent="handleSubmit"
|
||||
>
|
||||
<div class="flex-col-center mb-10 w-[90%] max-w-[300px] px-4">
|
||||
<EasyFlowAvatar :src="avatar" class="enter-x mb-6 size-20" />
|
||||
<div class="enter-x mb-2 w-full items-center">
|
||||
<Form />
|
||||
</div>
|
||||
<EasyFlowButton class="enter-x w-full" @click="handleSubmit">
|
||||
{{ $t('ui.widgets.lockScreen.entry') }}
|
||||
</EasyFlowButton>
|
||||
<EasyFlowButton
|
||||
class="enter-x my-2 w-full"
|
||||
variant="ghost"
|
||||
@click="$emit('toLogin')"
|
||||
>
|
||||
{{ $t('ui.widgets.lockScreen.backToLogin') }}
|
||||
</EasyFlowButton>
|
||||
<EasyFlowButton
|
||||
class="enter-x mr-2 w-full"
|
||||
variant="ghost"
|
||||
@click="toggleUnlockForm"
|
||||
>
|
||||
{{ $t('common.back') }}
|
||||
</EasyFlowButton>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div
|
||||
class="enter-y absolute bottom-5 w-full text-center text-xl md:text-2xl xl:text-xl 2xl:text-3xl"
|
||||
>
|
||||
<div v-if="showUnlockForm" class="enter-x mb-2 text-2xl md:text-3xl">
|
||||
{{ hour }}:{{ minute }}
|
||||
<span class="text-base md:text-lg">{{ meridiem }}</span>
|
||||
</div>
|
||||
<div class="text-xl md:text-3xl">{{ date }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user