lock-screen.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <script setup lang="ts">
  2. import { computed, reactive, ref, watchEffect } from 'vue';
  3. import { IcRoundLock } from '@vben-core/icons';
  4. import { $t, useI18n } from '@vben-core/locales';
  5. import {
  6. VbenAvatar,
  7. VbenButton,
  8. VbenInputPassword,
  9. } from '@vben-core/shadcn-ui';
  10. import { useDateFormat, useNow } from '@vueuse/core';
  11. interface Props {
  12. avatar?: string;
  13. cachedPassword?: string;
  14. }
  15. defineOptions({
  16. name: 'LockScreen',
  17. });
  18. const props = withDefaults(defineProps<Props>(), {
  19. avatar: '',
  20. cachedPassword: undefined,
  21. });
  22. const emit = defineEmits<{ toLogin: []; unlock: [string] }>();
  23. const { locale } = useI18n();
  24. const now = useNow();
  25. const meridiem = useDateFormat(now, 'A');
  26. const hour = useDateFormat(now, 'HH');
  27. const minute = useDateFormat(now, 'mm');
  28. const date = useDateFormat(now, 'YYYY-MM-DD dddd', { locales: locale.value });
  29. const showUnlockForm = ref(false);
  30. const validPass = ref(true);
  31. const formState = reactive({
  32. password: '',
  33. submitted: false,
  34. });
  35. const passwordStatus = computed(() => {
  36. if (formState.submitted && !formState.password) {
  37. return 'error';
  38. }
  39. if (formState.submitted && !validPass.value) {
  40. return 'error';
  41. }
  42. return 'default';
  43. });
  44. const errorTip = computed(() => {
  45. return props.cachedPassword === undefined || !formState.password
  46. ? $t('widgets.lockScreen.placeholder')
  47. : $t('widgets.lockScreen.errorPasswordTip');
  48. });
  49. watchEffect(() => {
  50. if (!formState.password) {
  51. validPass.value = true;
  52. }
  53. });
  54. function handleSubmit() {
  55. formState.submitted = true;
  56. if (passwordStatus.value !== 'default') {
  57. return;
  58. }
  59. if (props.cachedPassword !== formState.password) {
  60. validPass.value = false;
  61. return;
  62. }
  63. emit('unlock', formState.password);
  64. }
  65. function toggleUnlockForm() {
  66. showUnlockForm.value = !showUnlockForm.value;
  67. }
  68. </script>
  69. <template>
  70. <div class="bg-background fixed z-[2000] size-full">
  71. <transition name="slide-left">
  72. <div v-show="!showUnlockForm" class="size-full">
  73. <div
  74. class="flex-col-center text-foreground/80 hover:text-foreground group my-4 cursor-pointer text-xl font-semibold"
  75. @click="toggleUnlockForm"
  76. >
  77. <IcRoundLock
  78. class="size-5 transition-all duration-300 group-hover:scale-125"
  79. />
  80. <span>{{ $t('widgets.lockScreen.unlock') }}</span>
  81. </div>
  82. <div class="flex h-full justify-center px-[10%]">
  83. <div
  84. class="bg-accent flex-center relative mb-14 mr-20 h-4/5 w-2/5 flex-auto rounded-3xl text-center text-[260px]"
  85. >
  86. <span class="absolute left-4 top-4 text-xl font-semibold">
  87. {{ meridiem }}
  88. </span>
  89. {{ hour }}
  90. </div>
  91. <div
  92. class="bg-accent flex-center mb-14 h-4/5 w-2/5 flex-auto rounded-3xl text-center text-[260px]"
  93. >
  94. {{ minute }}
  95. </div>
  96. </div>
  97. </div>
  98. </transition>
  99. <transition name="slide-right">
  100. <div
  101. v-if="showUnlockForm"
  102. class="flex-center size-full"
  103. @keypress.enter.prevent="handleSubmit"
  104. >
  105. <div class="flex-col-center mb-10 w-[300px]">
  106. <VbenAvatar :src="avatar" class="enter-x mb-6 size-20" />
  107. <div class="enter-x mb-2 w-full items-center">
  108. <VbenInputPassword
  109. v-model="formState.password"
  110. :autofocus="true"
  111. :error-tip="errorTip"
  112. :label="$t('widgets.lockScreen.password')"
  113. :placeholder="$t('widgets.lockScreen.placeholder')"
  114. :status="passwordStatus"
  115. name="password"
  116. required
  117. type="password"
  118. />
  119. </div>
  120. <VbenButton class="enter-x w-full" @click="handleSubmit">
  121. {{ $t('widgets.lockScreen.entry') }}
  122. </VbenButton>
  123. <VbenButton
  124. class="enter-x my-2 w-full"
  125. variant="ghost"
  126. @click="$emit('toLogin')"
  127. >
  128. {{ $t('widgets.lockScreen.backToLogin') }}
  129. </VbenButton>
  130. <VbenButton
  131. class="enter-x mr-2 w-full"
  132. variant="ghost"
  133. @click="toggleUnlockForm"
  134. >
  135. {{ $t('common.back') }}
  136. </VbenButton>
  137. </div>
  138. </div>
  139. </transition>
  140. <div
  141. class="enter-y absolute bottom-5 w-full text-center text-gray-300 xl:text-xl 2xl:text-3xl"
  142. >
  143. <div v-if="showUnlockForm" class="enter-x mb-2 text-3xl">
  144. {{ hour }}:{{ minute }} <span class="text-lg">{{ meridiem }}</span>
  145. </div>
  146. <div class="text-3xl">{{ date }}</div>
  147. </div>
  148. </div>
  149. </template>