diff --git a/web/src/i18n/locales/en.ts b/web/src/i18n/locales/en.ts index a69f76f..a6cea2f 100644 --- a/web/src/i18n/locales/en.ts +++ b/web/src/i18n/locales/en.ts @@ -173,7 +173,8 @@ const en = { reset: 'Reset', power: 'Power', powerShort: 'Power (short click)', - powerLong: 'Power (long click)' + powerLong: 'Power (long click)', + powerConfirm: 'Are you sure you want to perform this operation?' }, settings: { title: 'Settings', @@ -225,9 +226,14 @@ const en = { tip: 'Enable the default swap partition(128M)' }, mouseJiggler: { - title: "Mouse Jiggler", + title: 'Mouse Jiggler', description: 'Enable the mouse jiggler', - tip: "Automatically jiggles the mouse when inactive for 60 seconds to prevent system sleep" + tip: 'Automatically jiggles the mouse when inactive for 60 seconds to prevent system sleep' + }, + powerConfirm: { + title: 'Power Operation Reconfirmation', + description: 'Enable power operation reconfirmation', + tip: 'When operating the power, a double confirmation dialog will pop up to prevent accidental touches' }, mdns: { description: 'Enable mDNS discovery service', diff --git a/web/src/i18n/locales/zh.ts b/web/src/i18n/locales/zh.ts index 803e9e9..2abdcca 100644 --- a/web/src/i18n/locales/zh.ts +++ b/web/src/i18n/locales/zh.ts @@ -23,6 +23,8 @@ const zh = { illegalUsername: '用户名中包含非法字符', illegalPassword: '密码中包含非法字符', forgetPassword: '忘记密码', + reboot: '重启', + rebootDesc: '你确定要重启NanoKVM吗?', ok: '确定', cancel: '取消', loginButtonText: '登录', @@ -157,7 +159,8 @@ const zh = { reset: '重启', power: '电源', powerShort: '电源(短按)', - powerLong: '电源(长按)' + powerLong: '电源(长按)', + powerConfirm: '你确定要执行此操作吗?' }, settings: { title: '设置', @@ -213,6 +216,11 @@ const zh = { description: '启用鼠标抖动', tip: '在鼠标离开60s未操作时自动进行抖动以防止系统睡眠' }, + powerConfirm:{ + title: '电源操作确认', + description: '启用电源操作二次确认', + tip: '在操作电源时弹出二次确认框以防误触' + }, mdns: { description: '启用 mDNS 发现服务', tip: '如果您未使用此功能,建议将其关闭' @@ -221,7 +229,9 @@ const zh = { disk: '虚拟U盘', diskDesc: '在远程主机中挂载虚拟U盘', network: '虚拟网卡', - networkDesc: '在远程主机中挂载虚拟网卡' + networkDesc: '在远程主机中挂载虚拟网卡', + okBtn: '是', + cancelBtn: '否' }, tailscale: { title: 'Tailscale', diff --git a/web/src/lib/localstorage.ts b/web/src/lib/localstorage.ts index 07cbc1e..980f20e 100644 --- a/web/src/lib/localstorage.ts +++ b/web/src/lib/localstorage.ts @@ -13,6 +13,7 @@ const SKIP_UPDATE_KEY = 'nano-kvm-check-update'; const KEYBOARD_LAYOUT_KEY = 'nano-kvm-keyboard-layout'; const SKIP_MODIFY_PASSWORD_KEY = 'nano-kvm-skip-modify-password'; const MENU_DISABLED_ITEMS_KEY = 'nano-kvm-menu-disabled-items'; +const POWER_CONFIRM_KEY = 'nano-kvm-power-confirm'; type ItemWithExpiry = { value: string; @@ -165,3 +166,12 @@ export function getMenuDisabledItems(): string[] { const value = localStorage.getItem(MENU_DISABLED_ITEMS_KEY); return value ? JSON.parse(value) : []; } + +export function getPowerConfirm(): string | null { + return getWithExpiry(POWER_CONFIRM_KEY); +} + +export function setPowerConfirm(value: boolean) { + const expiry = 365 * 24 * 60 * 60 * 1000; + setWithExpiry(POWER_CONFIRM_KEY, String(value), expiry); +} \ No newline at end of file diff --git a/web/src/pages/desktop/menu/power/index.tsx b/web/src/pages/desktop/menu/power/index.tsx index 2469fa1..e8ebc02 100644 --- a/web/src/pages/desktop/menu/power/index.tsx +++ b/web/src/pages/desktop/menu/power/index.tsx @@ -1,8 +1,9 @@ import { useEffect, useState } from 'react'; -import { Slider } from 'antd'; +import { Popconfirm, Slider } from 'antd'; import clsx from 'clsx'; import { CirclePowerIcon, LoaderCircleIcon, PowerIcon, RotateCcwIcon } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { getPowerConfirm } from '../../../../lib/localstorage' import * as api from '@/api/vm'; import { MenuItem } from '@/components/menu-item.tsx'; @@ -13,10 +14,10 @@ export const Power = () => { const [isLoading, setIsLoading] = useState(false); const [powerDuration, setPowerDuration] = useState(8); + useEffect(() => { getLed(); const interval = setInterval(getLed, 5000); - return () => { clearInterval(interval); }; @@ -33,7 +34,10 @@ export const Power = () => { }); } - function click(button: 'reset' | 'power', duration?: number) { + function click(button: 'reset' | 'power',type: string, duration?: number) { + + if(getPowerConfirm() == 'true' && type == 'first') return; + if (isLoading) return; setIsLoading(true); @@ -48,6 +52,8 @@ export const Power = () => { setPowerDuration(value); } + + const icon = (