feat: add the ability to lock the screen (#30)
* feat: 锁屏功能 * feat: 锁屏样式调整 * feat: complete the lock-screen screen and support shortcut keys and preference configuration --------- Co-authored-by: vince <vince292007@gmail.com>pull/48/MERGE
parent
61dbb05b5d
commit
06f5d5686d
|
@ -1,11 +1,12 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, toRefs } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { LOGIN_PATH } from '@vben/constants';
|
||||||
import { IcRoundCreditScore, MdiDriveDocument, MdiGithub } from '@vben/icons';
|
import { IcRoundCreditScore, MdiDriveDocument, MdiGithub } from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
|
LockScreen,
|
||||||
Notification,
|
Notification,
|
||||||
NotificationItem,
|
NotificationItem,
|
||||||
UserDropdown,
|
UserDropdown,
|
||||||
|
@ -16,7 +17,7 @@ import { preferences } from '@vben-core/preferences';
|
||||||
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { resetRoutes } from '#/router';
|
import { resetRoutes } from '#/router';
|
||||||
import { useAccessStore, useAppStore } from '#/store';
|
import { storeToRefs, useAccessStore, useAppStore } from '#/store';
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
|
@ -85,11 +86,18 @@ const menus = computed(() => [
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
|
const { isLockScreen, lockScreenPassword } = storeToRefs(appStore);
|
||||||
const {
|
const {
|
||||||
loading: loginLoading,
|
loading: loginLoading,
|
||||||
openLoginExpiredModal,
|
openLoginExpiredModal,
|
||||||
userInfo,
|
userInfo,
|
||||||
} = toRefs(accessStore);
|
} = storeToRefs(accessStore);
|
||||||
|
|
||||||
|
const avatar = computed(() => {
|
||||||
|
return userInfo.value?.avatar ?? preferences.app.defaultAvatar;
|
||||||
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
|
@ -105,17 +113,22 @@ function handleNoticeClear() {
|
||||||
function handleMakeAll() {
|
function handleMakeAll() {
|
||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleLockScreen(password: string) {
|
||||||
|
appStore.lockScreen(password);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
||||||
<template #user-dropdown>
|
<template #user-dropdown>
|
||||||
<UserDropdown
|
<UserDropdown
|
||||||
:avatar="userInfo?.avatar ?? preferences.app.defaultAvatar"
|
:avatar
|
||||||
:menus="menus"
|
:menus
|
||||||
:text="userInfo?.realName"
|
:text="userInfo?.realName"
|
||||||
description="ann.vben@gmail.com"
|
description="ann.vben@gmail.com"
|
||||||
tag-text="Pro"
|
tag-text="Pro"
|
||||||
|
@lock-screen="handleLockScreen"
|
||||||
@logout="handleLogout"
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -127,7 +140,7 @@ function handleMakeAll() {
|
||||||
@make-all="handleMakeAll"
|
@make-all="handleMakeAll"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #dialog>
|
<template #extra>
|
||||||
<AuthenticationLoginExpiredModal
|
<AuthenticationLoginExpiredModal
|
||||||
v-model:open="openLoginExpiredModal"
|
v-model:open="openLoginExpiredModal"
|
||||||
:loading="loginLoading"
|
:loading="loginLoading"
|
||||||
|
@ -136,5 +149,14 @@ function handleMakeAll() {
|
||||||
@submit="accessStore.authLogin"
|
@submit="accessStore.authLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template #lock-screen>
|
||||||
|
<LockScreen
|
||||||
|
v-if="isLockScreen"
|
||||||
|
:avatar
|
||||||
|
:cached-password="lockScreenPassword"
|
||||||
|
@to-login="handleLogout"
|
||||||
|
@unlock="appStore.unlockScreen"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</BasicLayout>
|
</BasicLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -51,7 +51,7 @@ const essentialsRoutes: RouteRecordRaw[] = [
|
||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_essential/authentication/code-login.vue'),
|
import('#/views/_essential/authentication/code-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.essentials.code-login'),
|
title: $t('page.essentials.codeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { InitStoreOptions } from '@vben-core/stores';
|
||||||
|
|
||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
|
|
||||||
import { initStore } from '@vben-core/stores';
|
import { initStore, storeToRefs } from '@vben-core/stores';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @zh_CN 初始化pinia
|
* @zh_CN 初始化pinia
|
||||||
|
@ -13,7 +13,7 @@ async function setupStore(app: App, options: InitStoreOptions) {
|
||||||
app.use(pinia);
|
app.use(pinia);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { setupStore };
|
export { setupStore, storeToRefs };
|
||||||
|
|
||||||
export { useAccessStore } from './modules/access';
|
export { useAccessStore } from './modules/access';
|
||||||
export { useAppStore } from './modules/app';
|
export { useAppStore } from './modules/app';
|
||||||
|
|
|
@ -4,19 +4,35 @@ import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { useAccessStore } from './access';
|
import { useAccessStore } from './access';
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', () => {
|
interface AppState {
|
||||||
const accessStore = useAccessStore();
|
isLockScreen: boolean;
|
||||||
const coreTabbarStore = useCoreTabbarStore();
|
lockScreenPassword?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
export const useAppStore = defineStore('app', {
|
||||||
* 重置所有状态
|
actions: {
|
||||||
*/
|
lockScreen(password: string) {
|
||||||
async function resetAppState() {
|
this.isLockScreen = true;
|
||||||
accessStore.reset();
|
this.lockScreenPassword = password;
|
||||||
coreTabbarStore.$reset();
|
},
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
resetAppState() {
|
||||||
resetAppState,
|
const accessStore = useAccessStore();
|
||||||
};
|
const coreTabbarStore = useCoreTabbarStore();
|
||||||
|
accessStore.reset();
|
||||||
|
coreTabbarStore.$reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
unlockScreen() {
|
||||||
|
this.isLockScreen = false;
|
||||||
|
this.lockScreenPassword = undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
persist: {
|
||||||
|
paths: ['isLockScreen', 'lockScreenPassword'],
|
||||||
|
},
|
||||||
|
state: (): AppState => ({
|
||||||
|
isLockScreen: false,
|
||||||
|
lockScreenPassword: undefined,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,6 @@ import type { Preferences } from './types';
|
||||||
const defaultPreferences: Preferences = {
|
const defaultPreferences: Preferences = {
|
||||||
app: {
|
app: {
|
||||||
accessMode: 'frontend',
|
accessMode: 'frontend',
|
||||||
aiAssistant: true,
|
|
||||||
authPageLayout: 'panel-right',
|
authPageLayout: 'panel-right',
|
||||||
colorGrayMode: false,
|
colorGrayMode: false,
|
||||||
colorWeakMode: false,
|
colorWeakMode: false,
|
||||||
|
@ -55,6 +54,7 @@ const defaultPreferences: Preferences = {
|
||||||
},
|
},
|
||||||
shortcutKeys: {
|
shortcutKeys: {
|
||||||
enable: true,
|
enable: true,
|
||||||
|
globalLockScreen: true,
|
||||||
globalLogout: true,
|
globalLogout: true,
|
||||||
globalPreferences: true,
|
globalPreferences: true,
|
||||||
globalSearch: true,
|
globalSearch: true,
|
||||||
|
@ -95,6 +95,7 @@ const defaultPreferences: Preferences = {
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
globalSearch: true,
|
globalSearch: true,
|
||||||
languageToggle: true,
|
languageToggle: true,
|
||||||
|
lockScreen: true,
|
||||||
notification: true,
|
notification: true,
|
||||||
sidebarToggle: true,
|
sidebarToggle: true,
|
||||||
themeToggle: true,
|
themeToggle: true,
|
||||||
|
|
|
@ -26,8 +26,6 @@ type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right';
|
||||||
interface AppPreferences {
|
interface AppPreferences {
|
||||||
/** 权限模式 */
|
/** 权限模式 */
|
||||||
accessMode: AccessModeType;
|
accessMode: AccessModeType;
|
||||||
/** 是否开启vben助手 */
|
|
||||||
aiAssistant: boolean;
|
|
||||||
/** 登录注册页面布局 */
|
/** 登录注册页面布局 */
|
||||||
authPageLayout: AuthPageLayoutType;
|
authPageLayout: AuthPageLayoutType;
|
||||||
/** 是否开启灰色模式 */
|
/** 是否开启灰色模式 */
|
||||||
|
@ -136,6 +134,8 @@ interface SidebarPreferences {
|
||||||
interface ShortcutKeyPreferences {
|
interface ShortcutKeyPreferences {
|
||||||
/** 是否启用快捷键-全局 */
|
/** 是否启用快捷键-全局 */
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
|
/** 是否启用全局锁屏快捷键 */
|
||||||
|
globalLockScreen: boolean;
|
||||||
/** 是否启用全局注销快捷键 */
|
/** 是否启用全局注销快捷键 */
|
||||||
globalLogout: boolean;
|
globalLogout: boolean;
|
||||||
/** 是否启用全局偏好设置快捷键 */
|
/** 是否启用全局偏好设置快捷键 */
|
||||||
|
@ -194,6 +194,8 @@ interface WidgetPreferences {
|
||||||
globalSearch: boolean;
|
globalSearch: boolean;
|
||||||
/** 是否启用语言切换部件 */
|
/** 是否启用语言切换部件 */
|
||||||
languageToggle: boolean;
|
languageToggle: boolean;
|
||||||
|
/** 是否开启锁屏功能 */
|
||||||
|
lockScreen: boolean;
|
||||||
/** 是否显示通知部件 */
|
/** 是否显示通知部件 */
|
||||||
notification: boolean;
|
notification: boolean;
|
||||||
/** 是否显示侧边栏显示/隐藏部件 */
|
/** 是否显示侧边栏显示/隐藏部件 */
|
||||||
|
|
|
@ -125,6 +125,11 @@ function usePreferences() {
|
||||||
return enable && globalLogout;
|
return enable && globalLogout;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const globalLockScreenShortcutKey = computed(() => {
|
||||||
|
const { enable, globalLockScreen } = shortcutKeysPreferences.value;
|
||||||
|
return enable && globalLockScreen;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @zh_CN 是否启用全局偏好设置快捷键
|
* @zh_CN 是否启用全局偏好设置快捷键
|
||||||
*/
|
*/
|
||||||
|
@ -138,6 +143,7 @@ function usePreferences() {
|
||||||
authPanelLeft,
|
authPanelLeft,
|
||||||
authPanelRight,
|
authPanelRight,
|
||||||
diffPreference,
|
diffPreference,
|
||||||
|
globalLockScreenShortcutKey,
|
||||||
globalLogoutShortcutKey,
|
globalLogoutShortcutKey,
|
||||||
globalPreferencesShortcutKey,
|
globalPreferencesShortcutKey,
|
||||||
globalSearchShortcutKey,
|
globalSearchShortcutKey,
|
||||||
|
|
|
@ -68,6 +68,16 @@
|
||||||
"noResults": "No Search Results Found",
|
"noResults": "No Search Results Found",
|
||||||
"noRecent": "No Search History",
|
"noRecent": "No Search History",
|
||||||
"recent": "Search History"
|
"recent": "Search History"
|
||||||
|
},
|
||||||
|
"lockScreen": {
|
||||||
|
"title": "Lock Screen",
|
||||||
|
"screenButton": "Locking",
|
||||||
|
"password": "Password",
|
||||||
|
"placeholder": "Please enter password",
|
||||||
|
"unlock": "Click to unlock",
|
||||||
|
"errorPasswordTip": "Password error, please re-enter",
|
||||||
|
"backToLogin": "Back to login",
|
||||||
|
"entry": "Enter the system"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
|
@ -263,7 +273,8 @@
|
||||||
"languageToggle": "Enable Language Toggle",
|
"languageToggle": "Enable Language Toggle",
|
||||||
"notification": "Enable Notification",
|
"notification": "Enable Notification",
|
||||||
"sidebarToggle": "Enable Sidebar Toggle",
|
"sidebarToggle": "Enable Sidebar Toggle",
|
||||||
"aiAssistant": "Enable AI Assistant"
|
"aiAssistant": "Enable AI Assistant",
|
||||||
|
"lockScreen": "Enable Lock Screen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,16 @@
|
||||||
"noResults": "未找到搜索结果",
|
"noResults": "未找到搜索结果",
|
||||||
"noRecent": "没有搜索历史",
|
"noRecent": "没有搜索历史",
|
||||||
"recent": "搜索历史"
|
"recent": "搜索历史"
|
||||||
|
},
|
||||||
|
"lockScreen": {
|
||||||
|
"title": "锁定屏幕",
|
||||||
|
"screenButton": "锁定",
|
||||||
|
"password": "密码",
|
||||||
|
"placeholder": "请输入锁屏密码",
|
||||||
|
"unlock": "点击解锁",
|
||||||
|
"errorPasswordTip": "密码错误,请重新输入",
|
||||||
|
"backToLogin": "返回登录",
|
||||||
|
"entry": "进入系统"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
|
@ -263,7 +273,8 @@
|
||||||
"languageToggle": "启用语言切换",
|
"languageToggle": "启用语言切换",
|
||||||
"notification": "启用通知",
|
"notification": "启用通知",
|
||||||
"sidebarToggle": "启用侧边栏切换",
|
"sidebarToggle": "启用侧边栏切换",
|
||||||
"aiAssistant": "启用 AI 助手"
|
"aiAssistant": "启用 AI 助手",
|
||||||
|
"lockScreen": "启用锁屏"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@forward './constants.scss';
|
@forward './constants';
|
||||||
|
|
||||||
@mixin b($block) {
|
@mixin b($block) {
|
||||||
$B: $namespace + '-' + $block !global;
|
$B: $namespace + '-' + $block !global;
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { h } from 'vue';
|
import { defineComponent, h } from 'vue';
|
||||||
|
|
||||||
import { Icon } from '@iconify/vue';
|
import { Icon } from '@iconify/vue';
|
||||||
|
|
||||||
function createIconifyIcon(icon: string) {
|
function createIconifyIcon(icon: string) {
|
||||||
return h(Icon, { icon });
|
return defineComponent({
|
||||||
|
name: `SvgIcon-${icon}`,
|
||||||
|
setup(props, { attrs }) {
|
||||||
|
return () => h(Icon, { icon, ...props, ...attrs });
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createIconifyIcon };
|
export { createIconifyIcon };
|
||||||
|
|
|
@ -82,3 +82,5 @@ export const IcRoundMultipleStop = createIconifyIcon('ic:round-multiple-stop');
|
||||||
export const IcRoundRefresh = createIconifyIcon('ic:round-refresh');
|
export const IcRoundRefresh = createIconifyIcon('ic:round-refresh');
|
||||||
|
|
||||||
export const IcRoundCreditScore = createIconifyIcon('ic:round-credit-score');
|
export const IcRoundCreditScore = createIconifyIcon('ic:round-credit-score');
|
||||||
|
|
||||||
|
export const IcRoundLock = createIconifyIcon('ic:round-lock');
|
||||||
|
|
|
@ -279,7 +279,10 @@ function clearPreferencesAndLogout() {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<slot name="dialog"></slot>
|
<slot name="extra"></slot>
|
||||||
|
<Transition v-if="preferences.widget.lockScreen" name="slide-up">
|
||||||
|
<slot name="lock-screen"></slot>
|
||||||
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
</VbenAdminLayout>
|
</VbenAdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -4,6 +4,7 @@ export { default as CozeAssistant } from './coze-assistant.vue';
|
||||||
export * from './global-search';
|
export * from './global-search';
|
||||||
export { default as LanguageToggle } from './language-toggle.vue';
|
export { default as LanguageToggle } from './language-toggle.vue';
|
||||||
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
|
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
|
||||||
|
export * from './lock-screen';
|
||||||
export * from './notification';
|
export * from './notification';
|
||||||
export * from './preferences';
|
export * from './preferences';
|
||||||
export * from './theme-toggle';
|
export * from './theme-toggle';
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as LockScreen } from './lock-screen.vue';
|
||||||
|
export { default as LockScreenModal } from './lock-screen-modal.vue';
|
|
@ -0,0 +1,106 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { RegisterEmits } from './typings';
|
||||||
|
|
||||||
|
import { computed, reactive } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
VbenAvatar,
|
||||||
|
VbenButton,
|
||||||
|
VbenInputPassword,
|
||||||
|
} from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
avatar?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'LockScreenModal',
|
||||||
|
});
|
||||||
|
withDefaults(defineProps<Props>(), {
|
||||||
|
avatar: '',
|
||||||
|
text: '',
|
||||||
|
});
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: RegisterEmits['submit'];
|
||||||
|
}>();
|
||||||
|
const formState = reactive({
|
||||||
|
lockScreenPassword: '',
|
||||||
|
submitted: false,
|
||||||
|
});
|
||||||
|
const openModal = defineModel<boolean>('open');
|
||||||
|
const passwordStatus = computed(() => {
|
||||||
|
return formState.submitted && !formState.lockScreenPassword
|
||||||
|
? 'error'
|
||||||
|
: 'default';
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
openModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
formState.submitted = true;
|
||||||
|
if (passwordStatus.value !== 'default') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('submit', {
|
||||||
|
lockScreenPassword: formState.lockScreenPassword,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Dialog :open="openModal">
|
||||||
|
<DialogContent
|
||||||
|
class="top-0 h-full w-full -translate-y-0 border-none p-0 shadow-xl sm:top-[20%] sm:h-[unset] sm:w-[600px] sm:rounded-2xl"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<DialogDescription />
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle
|
||||||
|
class="border-border flex h-8 items-center px-5 font-normal"
|
||||||
|
>
|
||||||
|
{{ $t('widgets.lockScreen.title') }}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div
|
||||||
|
class="mb-10 flex w-full flex-col items-center"
|
||||||
|
@keypress.enter.prevent="handleSubmit"
|
||||||
|
>
|
||||||
|
<div class="w-2/3">
|
||||||
|
<div class="ml-2 flex w-full flex-col items-center">
|
||||||
|
<VbenAvatar
|
||||||
|
:src="avatar"
|
||||||
|
class="size-24"
|
||||||
|
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>
|
||||||
|
<VbenInputPassword
|
||||||
|
v-model="formState.lockScreenPassword"
|
||||||
|
:error-tip="$t('widgets.lockScreen.placeholder')"
|
||||||
|
:label="$t('widgets.lockScreen.password')"
|
||||||
|
:placeholder="$t('widgets.lockScreen.placeholder')"
|
||||||
|
:status="passwordStatus"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
<VbenButton class="w-full" @click="handleSubmit">
|
||||||
|
{{ $t('widgets.lockScreen.screenButton') }}
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,170 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
import { IcRoundLock } from '@vben-core/iconify';
|
||||||
|
import { $t } from '@vben-core/locales';
|
||||||
|
import {
|
||||||
|
VbenAvatar,
|
||||||
|
VbenButton,
|
||||||
|
VbenInputPassword,
|
||||||
|
} from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
|
import { useDateFormat, useNow } from '@vueuse/core';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
avatar?: string;
|
||||||
|
cachedPassword?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'LockScreen',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
avatar: '',
|
||||||
|
cachedPassword: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{ toLogin: []; unlock: [string] }>();
|
||||||
|
|
||||||
|
const now = useNow();
|
||||||
|
const year = useDateFormat(now, 'YYYY');
|
||||||
|
const month = useDateFormat(now, 'MM');
|
||||||
|
const day = useDateFormat(now, 'DD');
|
||||||
|
const week = useDateFormat(now, 'dddd');
|
||||||
|
const hour = useDateFormat(now, 'HH');
|
||||||
|
const meridiem = useDateFormat(now, 'A');
|
||||||
|
const minute = useDateFormat(now, 'mm');
|
||||||
|
|
||||||
|
const showUnlockForm = ref(false);
|
||||||
|
const validPass = ref(true);
|
||||||
|
|
||||||
|
const formState = reactive({
|
||||||
|
password: '',
|
||||||
|
submitted: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordStatus = computed(() => {
|
||||||
|
if (formState.submitted && !formState.password) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formState.submitted && !validPass.value) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'default';
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorTip = computed(() => {
|
||||||
|
return props.cachedPassword === undefined || !formState.password
|
||||||
|
? $t('widgets.lockScreen.placeholder')
|
||||||
|
: $t('widgets.lockScreen.errorPasswordTip');
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!formState.password) {
|
||||||
|
validPass.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
formState.submitted = true;
|
||||||
|
if (passwordStatus.value !== 'default') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (props.cachedPassword !== formState.password) {
|
||||||
|
validPass.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('unlock', formState.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleUnlockForm() {
|
||||||
|
showUnlockForm.value = !showUnlockForm.value;
|
||||||
|
}
|
||||||
|
</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 my-4 cursor-pointer text-xl font-semibold"
|
||||||
|
@click="toggleUnlockForm"
|
||||||
|
>
|
||||||
|
<IcRoundLock
|
||||||
|
class="size-5 transition-all duration-300 group-hover:scale-125"
|
||||||
|
/>
|
||||||
|
<span>{{ $t('widgets.lockScreen.unlock') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex h-full justify-center px-[10%]">
|
||||||
|
<div
|
||||||
|
class="bg-accent flex-center relative mb-14 mr-20 h-4/5 w-2/5 flex-auto rounded-3xl text-center text-[260px]"
|
||||||
|
>
|
||||||
|
<span class="absolute left-4 top-4 text-xl font-semibold">{{
|
||||||
|
meridiem
|
||||||
|
}}</span>
|
||||||
|
{{ hour }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-accent flex-center mb-14 h-4/5 w-2/5 flex-auto rounded-3xl text-center text-[260px]"
|
||||||
|
>
|
||||||
|
{{ minute }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<transition name="slide-right">
|
||||||
|
<div
|
||||||
|
v-if="showUnlockForm"
|
||||||
|
class="flex-center size-full"
|
||||||
|
@keypress.enter.prevent="handleSubmit"
|
||||||
|
>
|
||||||
|
<div class="flex-col-center mb-10 w-[300px]">
|
||||||
|
<VbenAvatar :src="avatar" class="enter-x mb-6 size-20" />
|
||||||
|
<div class="items-cente enter-x mb-2 w-full">
|
||||||
|
<VbenInputPassword
|
||||||
|
v-model="formState.password"
|
||||||
|
:autofocus="true"
|
||||||
|
:error-tip="errorTip"
|
||||||
|
:label="$t('widgets.lockScreen.password')"
|
||||||
|
:placeholder="$t('widgets.lockScreen.placeholder')"
|
||||||
|
:status="passwordStatus"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<VbenButton class="enter-x w-full" @click="handleSubmit">
|
||||||
|
{{ $t('widgets.lockScreen.entry') }}
|
||||||
|
</VbenButton>
|
||||||
|
<VbenButton
|
||||||
|
class="enter-x my-2 w-full"
|
||||||
|
variant="ghost"
|
||||||
|
@click="$emit('toLogin')"
|
||||||
|
>
|
||||||
|
{{ $t('widgets.lockScreen.backToLogin') }}
|
||||||
|
</VbenButton>
|
||||||
|
<VbenButton
|
||||||
|
class="enter-x mr-2 w-full"
|
||||||
|
variant="ghost"
|
||||||
|
@click="toggleUnlockForm"
|
||||||
|
>
|
||||||
|
{{ $t('common.back') }}
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="enter-y absolute bottom-5 w-full text-center text-gray-300 xl:text-xl 2xl:text-3xl"
|
||||||
|
>
|
||||||
|
<div v-if="showUnlockForm" class="enter-x mb-2 text-3xl">
|
||||||
|
{{ hour }}:{{ minute }} <span class="text-lg">{{ meridiem }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-3xl">{{ year }}/{{ month }}/{{ day }} {{ week }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,9 @@
|
||||||
|
interface LockAndRegisterParams {
|
||||||
|
lockScreenPassword: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterEmits {
|
||||||
|
submit: [LockAndRegisterParams];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { LockAndRegisterParams, RegisterEmits };
|
|
@ -14,6 +14,7 @@ const widgetNotification = defineModel<boolean>('widgetNotification');
|
||||||
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
||||||
const widgetAiAssistant = defineModel<boolean>('widgetAiAssistant');
|
const widgetAiAssistant = defineModel<boolean>('widgetAiAssistant');
|
||||||
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
||||||
|
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -35,6 +36,9 @@ const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
||||||
<SwitchItem v-model="widgetAiAssistant">
|
<SwitchItem v-model="widgetAiAssistant">
|
||||||
{{ $t('preferences.widget.aiAssistant') }}
|
{{ $t('preferences.widget.aiAssistant') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
<SwitchItem v-model="widgetLockScreen">
|
||||||
|
{{ $t('preferences.widget.lockScreen') }}
|
||||||
|
</SwitchItem>
|
||||||
<SwitchItem v-model="widgetSidebarToggle">
|
<SwitchItem v-model="widgetSidebarToggle">
|
||||||
{{ $t('preferences.widget.sidebarToggle') }}
|
{{ $t('preferences.widget.sidebarToggle') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
|
|
@ -16,6 +16,7 @@ const shortcutKeysGlobalSearch = defineModel<boolean>(
|
||||||
);
|
);
|
||||||
const shortcutKeysLogout = defineModel<boolean>('shortcutKeysLogout');
|
const shortcutKeysLogout = defineModel<boolean>('shortcutKeysLogout');
|
||||||
const shortcutKeysPreferences = defineModel<boolean>('shortcutKeysPreferences');
|
const shortcutKeysPreferences = defineModel<boolean>('shortcutKeysPreferences');
|
||||||
|
const shortcutKeysLockScreen = defineModel<boolean>('shortcutKeysLockScreen');
|
||||||
|
|
||||||
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||||
</script>
|
</script>
|
||||||
|
@ -24,19 +25,26 @@ const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||||
<SwitchItem v-model="shortcutKeysEnable">
|
<SwitchItem v-model="shortcutKeysEnable">
|
||||||
{{ $t('preferences.shortcutKeys.title') }}
|
{{ $t('preferences.shortcutKeys.title') }}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysGlobalSearch">
|
<SwitchItem
|
||||||
|
v-model="shortcutKeysGlobalSearch"
|
||||||
|
:disabled="!shortcutKeysEnable"
|
||||||
|
>
|
||||||
{{ $t('preferences.shortcutKeys.search') }}
|
{{ $t('preferences.shortcutKeys.search') }}
|
||||||
<template #shortcut>
|
<template #shortcut>
|
||||||
{{ isWindowsOs() ? 'Ctrl' : '⌘' }}
|
{{ isWindowsOs() ? 'Ctrl' : '⌘' }}
|
||||||
<kbd> K </kbd>
|
<kbd> K </kbd>
|
||||||
</template>
|
</template>
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysLogout">
|
<SwitchItem v-model="shortcutKeysLogout" :disabled="!shortcutKeysEnable">
|
||||||
{{ $t('preferences.shortcutKeys.logout') }}
|
{{ $t('preferences.shortcutKeys.logout') }}
|
||||||
<template #shortcut> {{ altView }} Q </template>
|
<template #shortcut> {{ altView }} Q </template>
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
<SwitchItem v-if="shortcutKeysEnable" v-model="shortcutKeysPreferences">
|
<SwitchItem v-model="shortcutKeysPreferences" :disabled="!shortcutKeysEnable">
|
||||||
{{ $t('preferences.shortcutKeys.preferences') }}
|
{{ $t('preferences.shortcutKeys.preferences') }}
|
||||||
<template #shortcut> {{ altView }} , </template>
|
<template #shortcut> {{ altView }} , </template>
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
<SwitchItem v-model="shortcutKeysLockScreen" :disabled="!shortcutKeysEnable">
|
||||||
|
{{ $t('widgets.lockScreen.title') }}
|
||||||
|
<template #shortcut> {{ altView }} L </template>
|
||||||
|
</SwitchItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -128,6 +128,9 @@ const shortcutKeysGlobalLogout = defineModel<boolean>(
|
||||||
const shortcutKeysGlobalPreferences = defineModel<boolean>(
|
const shortcutKeysGlobalPreferences = defineModel<boolean>(
|
||||||
'shortcutKeysGlobalPreferences',
|
'shortcutKeysGlobalPreferences',
|
||||||
);
|
);
|
||||||
|
const shortcutKeysGlobalLockScreen = defineModel<boolean>(
|
||||||
|
'shortcutKeysGlobalLockScreen',
|
||||||
|
);
|
||||||
|
|
||||||
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
|
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
|
||||||
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
|
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
|
||||||
|
@ -136,6 +139,7 @@ const widgetNotification = defineModel<boolean>('widgetNotification');
|
||||||
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
|
||||||
const widgetAiAssistant = defineModel<boolean>('widgetAiAssistant');
|
const widgetAiAssistant = defineModel<boolean>('widgetAiAssistant');
|
||||||
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
|
||||||
|
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
diffPreference,
|
diffPreference,
|
||||||
|
@ -355,6 +359,7 @@ async function handleReset() {
|
||||||
v-model:widget-fullscreen="widgetFullscreen"
|
v-model:widget-fullscreen="widgetFullscreen"
|
||||||
v-model:widget-global-search="widgetGlobalSearch"
|
v-model:widget-global-search="widgetGlobalSearch"
|
||||||
v-model:widget-language-toggle="widgetLanguageToggle"
|
v-model:widget-language-toggle="widgetLanguageToggle"
|
||||||
|
v-model:widget-lock-screen="widgetLockScreen"
|
||||||
v-model:widget-notification="widgetNotification"
|
v-model:widget-notification="widgetNotification"
|
||||||
v-model:widget-sidebar-toggle="widgetSidebarToggle"
|
v-model:widget-sidebar-toggle="widgetSidebarToggle"
|
||||||
v-model:widget-theme-toggle="widgetThemeToggle"
|
v-model:widget-theme-toggle="widgetThemeToggle"
|
||||||
|
@ -384,6 +389,7 @@ async function handleReset() {
|
||||||
<GlobalShortcutKeys
|
<GlobalShortcutKeys
|
||||||
v-model:shortcut-keys-enable="shortcutKeysEnable"
|
v-model:shortcut-keys-enable="shortcutKeysEnable"
|
||||||
v-model:shortcut-keys-global-search="shortcutKeysGlobalSearch"
|
v-model:shortcut-keys-global-search="shortcutKeysGlobalSearch"
|
||||||
|
v-model:shortcut-keys-lock-screen="shortcutKeysGlobalLockScreen"
|
||||||
v-model:shortcut-keys-logout="shortcutKeysGlobalLogout"
|
v-model:shortcut-keys-logout="shortcutKeysGlobalLogout"
|
||||||
v-model:shortcut-keys-preferences="
|
v-model:shortcut-keys-preferences="
|
||||||
shortcutKeysGlobalPreferences
|
shortcutKeysGlobalPreferences
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Preferences from './preferences-sheet.vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* preferences 转成 vue props
|
* preferences 转成 vue props
|
||||||
* preferences.app.aiAssistant=>appAiAssistant
|
* preferences.widget.aiAssistant=>widgetAiAssistant
|
||||||
*/
|
*/
|
||||||
const attrs = computed(() => {
|
const attrs = computed(() => {
|
||||||
const result: Record<string, any> = {};
|
const result: Record<string, any> = {};
|
||||||
|
@ -22,7 +22,7 @@ const attrs = computed(() => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* preferences 转成 vue listener
|
* preferences 转成 vue listener
|
||||||
* preferences.app.aiAssistant=>@update:appAiAssistant
|
* preferences.widget.aiAssistant=>@update:widgetAiAssistant
|
||||||
*/
|
*/
|
||||||
const listen = computed(() => {
|
const listen = computed(() => {
|
||||||
const result: Record<string, any> = {};
|
const result: Record<string, any> = {};
|
||||||
|
|
|
@ -4,7 +4,11 @@ import type { AnyFunction } from '@vben/types';
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { IcRoundLogout, IcRoundSettingsSuggest } from '@vben-core/iconify';
|
import {
|
||||||
|
IcRoundLock,
|
||||||
|
IcRoundLogout,
|
||||||
|
IcRoundSettingsSuggest,
|
||||||
|
} from '@vben-core/iconify';
|
||||||
import { $t } from '@vben-core/locales';
|
import { $t } from '@vben-core/locales';
|
||||||
import { preferences, usePreferences } from '@vben-core/preferences';
|
import { preferences, usePreferences } from '@vben-core/preferences';
|
||||||
import {
|
import {
|
||||||
|
@ -24,6 +28,7 @@ import { isWindowsOs } from '@vben-core/toolkit';
|
||||||
|
|
||||||
import { useMagicKeys, whenever } from '@vueuse/core';
|
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||||
|
|
||||||
|
import { LockScreenModal } from '../lock-screen';
|
||||||
import { useOpenPreferences } from '../preferences';
|
import { useOpenPreferences } from '../preferences';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -68,12 +73,16 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
text: '',
|
text: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ logout: [] }>();
|
const emit = defineEmits<{ lockScreen: [string]; logout: [] }>();
|
||||||
const openPopover = ref(false);
|
const openPopover = ref(false);
|
||||||
const openDialog = ref(false);
|
const openDialog = ref(false);
|
||||||
|
const openLock = ref(false);
|
||||||
|
|
||||||
const { globalLogoutShortcutKey, globalPreferencesShortcutKey } =
|
const {
|
||||||
usePreferences();
|
globalLockScreenShortcutKey,
|
||||||
|
globalLogoutShortcutKey,
|
||||||
|
globalPreferencesShortcutKey,
|
||||||
|
} = usePreferences();
|
||||||
const { handleOpenPreference } = useOpenPreferences();
|
const { handleOpenPreference } = useOpenPreferences();
|
||||||
|
|
||||||
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
|
||||||
|
@ -82,6 +91,10 @@ const enableLogoutShortcutKey = computed(() => {
|
||||||
return props.enableShortcutKey && globalLogoutShortcutKey.value;
|
return props.enableShortcutKey && globalLogoutShortcutKey.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const enableLockScreenShortcutKey = computed(() => {
|
||||||
|
return props.enableShortcutKey && globalLockScreenShortcutKey.value;
|
||||||
|
});
|
||||||
|
|
||||||
const enableShortcutKey = computed(() => {
|
const enableShortcutKey = computed(() => {
|
||||||
return props.enableShortcutKey && preferences.shortcutKeys.enable;
|
return props.enableShortcutKey && preferences.shortcutKeys.enable;
|
||||||
});
|
});
|
||||||
|
@ -90,6 +103,18 @@ const enablePreferencesShortcutKey = computed(() => {
|
||||||
return props.enableShortcutKey && globalPreferencesShortcutKey.value;
|
return props.enableShortcutKey && globalPreferencesShortcutKey.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleOpenLock() {
|
||||||
|
openLock.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmitLock({
|
||||||
|
lockScreenPassword,
|
||||||
|
}: {
|
||||||
|
lockScreenPassword: string;
|
||||||
|
}) {
|
||||||
|
openLock.value = false;
|
||||||
|
emit('lockScreen', lockScreenPassword);
|
||||||
|
}
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
// emit
|
// emit
|
||||||
openDialog.value = true;
|
openDialog.value = true;
|
||||||
|
@ -114,10 +139,23 @@ if (enableShortcutKey.value) {
|
||||||
handleOpenPreference();
|
handleOpenPreference();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
whenever(keys['Alt+KeyL'], () => {
|
||||||
|
if (enableLockScreenShortcutKey.value) {
|
||||||
|
handleOpenLock();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<LockScreenModal
|
||||||
|
v-if="preferences.widget.lockScreen"
|
||||||
|
v-model:open="openLock"
|
||||||
|
:avatar="avatar"
|
||||||
|
:text="text"
|
||||||
|
@submit="handleSubmitLock"
|
||||||
|
/>
|
||||||
<VbenAlertDialog
|
<VbenAlertDialog
|
||||||
v-model:open="openDialog"
|
v-model:open="openDialog"
|
||||||
:cancel-text="$t('common.cancel')"
|
:cancel-text="$t('common.cancel')"
|
||||||
|
@ -180,6 +218,17 @@ if (enableShortcutKey.value) {
|
||||||
{{ altView }} ,
|
{{ altView }} ,
|
||||||
</DropdownMenuShortcut>
|
</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-if="preferences.widget.lockScreen"
|
||||||
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
@click="handleOpenLock"
|
||||||
|
>
|
||||||
|
<IcRoundLock class="mr-2 size-5" />
|
||||||
|
{{ $t('widgets.lockScreen.title') }}
|
||||||
|
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
|
||||||
|
{{ altView }} L
|
||||||
|
</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
|
|
|
@ -82,7 +82,7 @@ function handleSubmit() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function goLogin() {
|
function goToLogin() {
|
||||||
router.push(props.loginPath);
|
router.push(props.loginPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ onBeforeUnmount(() => {
|
||||||
<VbenButton :loading="loading" class="mt-2 w-full" @click="handleSubmit">
|
<VbenButton :loading="loading" class="mt-2 w-full" @click="handleSubmit">
|
||||||
{{ $t('common.login') }}
|
{{ $t('common.login') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
<VbenButton class="mt-4 w-full" variant="outline" @click="goLogin()">
|
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
|
||||||
{{ $t('common.back') }}
|
{{ $t('common.back') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,7 +50,7 @@ function handleSubmut() {
|
||||||
emit('submit', formState.email);
|
emit('submit', formState.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
function goLogin() {
|
function goToLogin() {
|
||||||
router.push(props.loginPath);
|
router.push(props.loginPath);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -79,7 +79,7 @@ function goLogin() {
|
||||||
<VbenButton class="mt-2 w-full" @click="handleSubmut">
|
<VbenButton class="mt-2 w-full" @click="handleSubmut">
|
||||||
{{ $t('authentication.sendResetLink') }}
|
{{ $t('authentication.sendResetLink') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
<VbenButton class="mt-4 w-full" variant="outline" @click="goLogin()">
|
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
|
||||||
{{ $t('common.back') }}
|
{{ $t('common.back') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,7 +39,7 @@ const qrcode = useQRCode(text, {
|
||||||
margin: 4,
|
margin: 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
function goLogin() {
|
function goToLogin() {
|
||||||
router.push(props.loginPath);
|
router.push(props.loginPath);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,7 +62,7 @@ function goLogin() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VbenButton class="mt-4 w-full" variant="outline" @click="goLogin()">
|
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
|
||||||
{{ $t('common.back') }}
|
{{ $t('common.back') }}
|
||||||
</VbenButton>
|
</VbenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -78,7 +78,7 @@ function handleSubmit() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function goLogin() {
|
function goToLogin() {
|
||||||
router.push(props.loginPath);
|
router.push(props.loginPath);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -160,7 +160,7 @@ function goLogin() {
|
||||||
{{ $t('authentication.alreadyHaveAccount') }}
|
{{ $t('authentication.alreadyHaveAccount') }}
|
||||||
<span
|
<span
|
||||||
class="text-primary hover:text-primary-hover cursor-pointer text-sm font-normal"
|
class="text-primary hover:text-primary-hover cursor-pointer text-sm font-normal"
|
||||||
@click="goLogin()"
|
@click="goToLogin()"
|
||||||
>
|
>
|
||||||
{{ $t('authentication.goToLogin') }}
|
{{ $t('authentication.goToLogin') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Reference in New Issue