mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 17:31:45 +08:00
feat: 完善按配置隐藏控制台菜单选项
This commit is contained in:
@@ -58,6 +58,7 @@ const isCh9329Backend = computed(() => hidBackend.value.includes('ch9329'))
|
|||||||
const showMsd = computed(() => {
|
const showMsd = computed(() => {
|
||||||
return !!systemStore.msd?.available && !isCh9329Backend.value
|
return !!systemStore.msd?.available && !isCh9329Backend.value
|
||||||
})
|
})
|
||||||
|
const showAtx = computed(() => systemStore.atx?.available === true)
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mouseMode?: 'absolute' | 'relative'
|
mouseMode?: 'absolute' | 'relative'
|
||||||
@@ -65,8 +66,10 @@ const props = defineProps<{
|
|||||||
ttydRunning?: boolean
|
ttydRunning?: boolean
|
||||||
showTerminal?: boolean
|
showTerminal?: boolean
|
||||||
showComputerUse?: boolean
|
showComputerUse?: boolean
|
||||||
|
showPasteText?: boolean
|
||||||
}>()
|
}>()
|
||||||
const showStats = computed(() => (props.videoMode ?? 'mjpeg') !== 'mjpeg')
|
const showStats = computed(() => (props.videoMode ?? 'mjpeg') !== 'mjpeg')
|
||||||
|
const showPasteText = computed(() => props.showPasteText !== false)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'toggleFullscreen'): void
|
(e: 'toggleFullscreen'): void
|
||||||
@@ -108,11 +111,13 @@ const openFromOverflow = (setter: () => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openMobileAtx = () => openFromOverflow(() => {
|
const openMobileAtx = () => openFromOverflow(() => {
|
||||||
|
if (!showAtx.value) return
|
||||||
mobileAtxOpen.value = true
|
mobileAtxOpen.value = true
|
||||||
mobileAtxOpenTime.value = Date.now()
|
mobileAtxOpenTime.value = Date.now()
|
||||||
})
|
})
|
||||||
|
|
||||||
const openMobilePaste = () => openFromOverflow(() => {
|
const openMobilePaste = () => openFromOverflow(() => {
|
||||||
|
if (!showPasteText.value) return
|
||||||
mobilePasteOpen.value = true
|
mobilePasteOpen.value = true
|
||||||
mobilePasteOpenTime.value = Date.now()
|
mobilePasteOpenTime.value = Date.now()
|
||||||
})
|
})
|
||||||
@@ -192,11 +197,27 @@ watch(locale, () => {
|
|||||||
measureButtonWidths()
|
measureButtonWidths()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(showAtx, (visible) => {
|
||||||
|
if (!visible) {
|
||||||
|
atxOpen.value = false
|
||||||
|
mobileAtxOpen.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(showPasteText, (visible) => {
|
||||||
|
if (!visible) {
|
||||||
|
pasteOpen.value = false
|
||||||
|
mobilePasteOpen.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const RIGHT_FIXED_PX = 120
|
const RIGHT_FIXED_PX = 120
|
||||||
|
|
||||||
const collapsibleItems = computed(() => {
|
const collapsibleItems = computed(() => {
|
||||||
const items = ITEM_SPECS.slice(3).filter(item => {
|
const items = ITEM_SPECS.slice(3).filter(item => {
|
||||||
if (item.id === 'msd' && !showMsd.value) return false
|
if (item.id === 'msd' && !showMsd.value) return false
|
||||||
|
if (item.id === 'atx' && !showAtx.value) return false
|
||||||
|
if (item.id === 'paste' && !showPasteText.value) return false
|
||||||
if (item.id === 'stats' && !showStats.value) return false
|
if (item.id === 'stats' && !showStats.value) return false
|
||||||
if (item.id === 'terminal' && props.showTerminal === false) return false
|
if (item.id === 'terminal' && props.showTerminal === false) return false
|
||||||
return true
|
return true
|
||||||
@@ -294,7 +315,7 @@ const hasRightOverflow = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ATX Power Control - Adaptive -->
|
<!-- ATX Power Control - Adaptive -->
|
||||||
<div v-if="isVisible('atx')">
|
<div v-if="showAtx && isVisible('atx')">
|
||||||
<Popover v-model:open="atxOpen">
|
<Popover v-model:open="atxOpen">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<Button variant="ghost" size="sm" class="h-8 gap-1.5 text-xs">
|
<Button variant="ghost" size="sm" class="h-8 gap-1.5 text-xs">
|
||||||
@@ -315,7 +336,7 @@ const hasRightOverflow = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Paste Text - Adaptive -->
|
<!-- Paste Text - Adaptive -->
|
||||||
<div v-if="isVisible('paste')">
|
<div v-if="showPasteText && isVisible('paste')">
|
||||||
<Popover v-model:open="pasteOpen">
|
<Popover v-model:open="pasteOpen">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<Button variant="ghost" size="sm" class="h-8 gap-1.5 text-xs">
|
<Button variant="ghost" size="sm" class="h-8 gap-1.5 text-xs">
|
||||||
@@ -466,13 +487,13 @@ const hasRightOverflow = computed(() => {
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<!-- ATX -->
|
<!-- ATX -->
|
||||||
<DropdownMenuItem v-if="!isVisible('atx')" @click="openMobileAtx">
|
<DropdownMenuItem v-if="showAtx && !isVisible('atx')" @click="openMobileAtx">
|
||||||
<Power class="h-4 w-4 mr-2" />
|
<Power class="h-4 w-4 mr-2" />
|
||||||
{{ t('actionbar.power') }}
|
{{ t('actionbar.power') }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<!-- Paste -->
|
<!-- Paste -->
|
||||||
<DropdownMenuItem v-if="!isVisible('paste')" @click="openMobilePaste">
|
<DropdownMenuItem v-if="showPasteText && !isVisible('paste')" @click="openMobilePaste">
|
||||||
<ClipboardPaste class="h-4 w-4 mr-2" />
|
<ClipboardPaste class="h-4 w-4 mr-2" />
|
||||||
{{ t('actionbar.paste') }}
|
{{ t('actionbar.paste') }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@@ -511,7 +532,7 @@ const hasRightOverflow = computed(() => {
|
|||||||
|
|
||||||
<!-- Mobile ATX Sheet — used when ATX is opened from the overflow menu.
|
<!-- Mobile ATX Sheet — used when ATX is opened from the overflow menu.
|
||||||
A Sheet avoids the Popover anchor-positioning issues on mobile. -->
|
A Sheet avoids the Popover anchor-positioning issues on mobile. -->
|
||||||
<Sheet v-model:open="mobileAtxOpen">
|
<Sheet v-if="showAtx" v-model:open="mobileAtxOpen">
|
||||||
<SheetContent
|
<SheetContent
|
||||||
side="bottom"
|
side="bottom"
|
||||||
class="max-h-[90dvh] overflow-y-auto"
|
class="max-h-[90dvh] overflow-y-auto"
|
||||||
@@ -532,7 +553,7 @@ const hasRightOverflow = computed(() => {
|
|||||||
</Sheet>
|
</Sheet>
|
||||||
|
|
||||||
<!-- Mobile Paste Sheet — used when Paste is opened from the overflow menu. -->
|
<!-- Mobile Paste Sheet — used when Paste is opened from the overflow menu. -->
|
||||||
<Sheet v-model:open="mobilePasteOpen">
|
<Sheet v-if="showPasteText" v-model:open="mobilePasteOpen">
|
||||||
<SheetContent
|
<SheetContent
|
||||||
side="bottom"
|
side="bottom"
|
||||||
class="max-h-[90dvh] overflow-y-auto"
|
class="max-h-[90dvh] overflow-y-auto"
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useLocalStorage } from '@vueuse/core'
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
import type { RemovableRef } from '@vueuse/core'
|
import type { RemovableRef } from '@vueuse/core'
|
||||||
|
|
||||||
export type FeatureVisibilityKey = 'webTerminal' | 'computerUse'
|
export type FeatureVisibilityKey = 'webTerminal' | 'computerUse' | 'pasteText'
|
||||||
export type FeatureVisibility = Record<FeatureVisibilityKey, boolean>
|
export type FeatureVisibility = Record<FeatureVisibilityKey, boolean>
|
||||||
|
|
||||||
const DEFAULT_FEATURE_VISIBILITY: FeatureVisibility = {
|
const DEFAULT_FEATURE_VISIBILITY: FeatureVisibility = {
|
||||||
webTerminal: true,
|
webTerminal: true,
|
||||||
computerUse: true,
|
computerUse: true,
|
||||||
|
pasteText: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const featureVisibility = useLocalStorage<FeatureVisibility>(
|
const featureVisibility = useLocalStorage<FeatureVisibility>(
|
||||||
|
|||||||
@@ -715,6 +715,7 @@ export default {
|
|||||||
featureVisibility: 'Feature Visibility',
|
featureVisibility: 'Feature Visibility',
|
||||||
featureVisibilityDesc: 'Control which feature entry points are shown on the console page',
|
featureVisibilityDesc: 'Control which feature entry points are shown on the console page',
|
||||||
computerUseAgent: 'Computer Use Agent',
|
computerUseAgent: 'Computer Use Agent',
|
||||||
|
pasteText: 'Paste Text',
|
||||||
videoSettings: 'Video Capture',
|
videoSettings: 'Video Capture',
|
||||||
videoSettingsDesc: 'Configure capture device format, resolution and frame rate',
|
videoSettingsDesc: 'Configure capture device format, resolution and frame rate',
|
||||||
videoDevice: 'Video Device',
|
videoDevice: 'Video Device',
|
||||||
|
|||||||
@@ -714,6 +714,7 @@ export default {
|
|||||||
featureVisibility: '功能展示',
|
featureVisibility: '功能展示',
|
||||||
featureVisibilityDesc: '控制控制台页面显示的功能入口',
|
featureVisibilityDesc: '控制控制台页面显示的功能入口',
|
||||||
computerUseAgent: 'Computer Use Agent',
|
computerUseAgent: 'Computer Use Agent',
|
||||||
|
pasteText: '粘贴文本',
|
||||||
videoSettings: '视频采集',
|
videoSettings: '视频采集',
|
||||||
videoSettingsDesc: '配置视频采集设备的格式、分辨率与帧率',
|
videoSettingsDesc: '配置视频采集设备的格式、分辨率与帧率',
|
||||||
videoDevice: '视频设备',
|
videoDevice: '视频设备',
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ const featureVisibility = useFeatureVisibility()
|
|||||||
const terminalAvailable = computed(() => ttydStatus.value?.available !== false)
|
const terminalAvailable = computed(() => ttydStatus.value?.available !== false)
|
||||||
const showTerminal = computed(() => terminalAvailable.value && featureVisibility.value.webTerminal)
|
const showTerminal = computed(() => terminalAvailable.value && featureVisibility.value.webTerminal)
|
||||||
const showComputerUse = computed(() => featureVisibility.value.computerUse)
|
const showComputerUse = computed(() => featureVisibility.value.computerUse)
|
||||||
|
const showPasteText = computed(() => featureVisibility.value.pasteText)
|
||||||
|
|
||||||
const isDark = ref(document.documentElement.classList.contains('dark'))
|
const isDark = ref(document.documentElement.classList.contains('dark'))
|
||||||
|
|
||||||
@@ -2838,6 +2839,7 @@ onUnmounted(() => {
|
|||||||
:ttyd-running="ttydStatus?.running"
|
:ttyd-running="ttydStatus?.running"
|
||||||
:show-terminal="showTerminal"
|
:show-terminal="showTerminal"
|
||||||
:show-computer-use="showComputerUse"
|
:show-computer-use="showComputerUse"
|
||||||
|
:show-paste-text="showPasteText"
|
||||||
@toggle-fullscreen="toggleFullscreen"
|
@toggle-fullscreen="toggleFullscreen"
|
||||||
@toggle-stats="openStatsSheet"
|
@toggle-stats="openStatsSheet"
|
||||||
@toggle-virtual-keyboard="handleToggleVirtualKeyboard"
|
@toggle-virtual-keyboard="handleToggleVirtualKeyboard"
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Bot,
|
Bot,
|
||||||
|
ClipboardPaste,
|
||||||
TimerReset,
|
TimerReset,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
@@ -2851,6 +2852,13 @@ watch(isWindows, () => {
|
|||||||
</Label>
|
</Label>
|
||||||
<Switch id="feature-computer-use" v-model="featureVisibility.computerUse" />
|
<Switch id="feature-computer-use" v-model="featureVisibility.computerUse" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between gap-4 px-3 py-3">
|
||||||
|
<Label for="feature-paste-text" class="flex min-w-0 items-center gap-2 font-normal">
|
||||||
|
<ClipboardPaste class="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
|
<span class="truncate">{{ t('settings.pasteText') }}</span>
|
||||||
|
</Label>
|
||||||
|
<Switch id="feature-paste-text" v-model="featureVisibility.pasteText" />
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user