mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-09-13 02:54:26 +08:00
fix: 完善 MSD 错误提示;解决 MSD 文件名称过长问题;优化粘贴文本样式;优化所有下拉框样式;删除“画面静止时会自动降帧”提示
This commit is contained in:
@@ -71,7 +71,6 @@ import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { ButtonGroup } from '@/components/ui/button-group'
|
||||
@@ -151,6 +150,7 @@ const configStore = useConfigStore()
|
||||
const authStore = useAuthStore()
|
||||
const featureVisibility = useFeatureVisibility()
|
||||
const { theme, setTheme } = useTheme()
|
||||
const EMPTY_SELECT_VALUE = '__one-kvm-empty-select-value__'
|
||||
|
||||
const isWindows = computed(() => systemStore.platform?.mode === 'windows')
|
||||
|
||||
@@ -2927,38 +2927,57 @@ watch(isWindows, () => {
|
||||
<CardContent class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="video-device">{{ t('settings.videoDevice') }}</Label>
|
||||
<NativeSelect id="video-device" v-model="config.video_device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.selectDevice') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in devices.video" :key="dev.path" :value="dev.path">{{ formatVideoDeviceLabel(dev) }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="config.video_device"
|
||||
@update:model-value="value => config.video_device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="video-device" class="w-full"><SelectValue :placeholder="t('settings.selectDevice')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.selectDevice') }}</SelectItem>
|
||||
<SelectItem v-for="dev in devices.video" :key="dev.path" :value="dev.path">{{ formatVideoDeviceLabel(dev) }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="video-format">{{ t('settings.videoFormat') }}</Label>
|
||||
<NativeSelect id="video-format" v-model="config.video_format" class="w-full" :disabled="!config.video_device">
|
||||
<NativeSelectOption value="">{{ t('settings.selectFormat') }}</NativeSelectOption>
|
||||
<NativeSelectOption
|
||||
v-for="fmt in availableFormatOptions"
|
||||
:key="fmt.format"
|
||||
:value="fmt.format"
|
||||
:disabled="fmt.disabled"
|
||||
>
|
||||
{{ fmt.format }} - {{ fmt.description }}{{ fmt.disabled ? t('common.notSupportedYet') : '' }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="config.video_format"
|
||||
:disabled="!config.video_device"
|
||||
@update:model-value="value => config.video_format = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="video-format" class="w-full"><SelectValue :placeholder="t('settings.selectFormat')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.selectFormat') }}</SelectItem>
|
||||
<SelectItem
|
||||
v-for="fmt in availableFormatOptions"
|
||||
:key="fmt.format"
|
||||
:value="fmt.format"
|
||||
:disabled="fmt.disabled"
|
||||
>
|
||||
{{ fmt.format }} - {{ fmt.description }}{{ fmt.disabled ? t('common.notSupportedYet') : '' }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<Label for="video-resolution">{{ t('settings.resolution') }}</Label>
|
||||
<NativeSelect id="video-resolution" :model-value="`${config.video_width}x${config.video_height}`" class="w-full" :disabled="!config.video_format" @update:model-value="value => { const parts = String(value).split('x').map(Number); if (parts[0] && parts[1]) { config.video_width = parts[0]; config.video_height = parts[1]; } }">
|
||||
<NativeSelectOption v-for="res in availableResolutions" :key="`${res.width}x${res.height}`" :value="`${res.width}x${res.height}`">{{ res.width }}x{{ res.height }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select :model-value="`${config.video_width}x${config.video_height}`" :disabled="!config.video_format" @update:model-value="value => { const parts = String(value).split('x').map(Number); if (parts[0] && parts[1]) { config.video_width = parts[0]; config.video_height = parts[1]; } }">
|
||||
<SelectTrigger id="video-resolution" class="w-full"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="res in availableResolutions" :key="`${res.width}x${res.height}`" :value="`${res.width}x${res.height}`">{{ res.width }}x{{ res.height }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="video-fps">{{ t('settings.frameRate') }}</Label>
|
||||
<NativeSelect id="video-fps" :model-value="config.video_fps" class="w-full" :disabled="!config.video_format" @update:model-value="value => config.video_fps = Number(value)">
|
||||
<NativeSelectOption v-for="fps in availableFps" :key="fps" :value="fps">{{ formatFpsLabel(fps) }}</NativeSelectOption>
|
||||
<NativeSelectOption v-if="!availableFps.includes(config.video_fps)" :value="config.video_fps">{{ formatFpsLabel(config.video_fps) }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select :model-value="config.video_fps" :disabled="!config.video_format" @update:model-value="value => config.video_fps = Number(value)">
|
||||
<SelectTrigger id="video-fps" class="w-full"><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="fps in availableFps" :key="fps" :value="fps">{{ formatFpsLabel(fps) }}</SelectItem>
|
||||
<SelectItem v-if="!availableFps.includes(config.video_fps)" :value="config.video_fps">{{ formatFpsLabel(config.video_fps) }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -3077,10 +3096,16 @@ watch(isWindows, () => {
|
||||
</div>
|
||||
<div v-if="config.hid_backend === 'ch9329'" class="space-y-2">
|
||||
<Label for="serial-device">{{ t('settings.serialDevice') }}</Label>
|
||||
<NativeSelect id="serial-device" v-model="config.hid_serial_device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.selectDevice') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in devices.serial" :key="dev.path" :value="dev.path">{{ dev.name }} ({{ dev.path }})</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="config.hid_serial_device"
|
||||
@update:model-value="value => config.hid_serial_device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="serial-device" class="w-full"><SelectValue :placeholder="t('settings.selectDevice')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.selectDevice') }}</SelectItem>
|
||||
<SelectItem v-for="dev in devices.serial" :key="dev.path" :value="dev.path">{{ dev.name }} ({{ dev.path }})</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div v-if="config.hid_backend === 'ch9329'" class="space-y-2">
|
||||
<Label for="serial-baudrate">{{ t('settings.baudRate') }}</Label>
|
||||
@@ -3093,10 +3118,16 @@ watch(isWindows, () => {
|
||||
</div>
|
||||
<div v-if="config.hid_backend === 'otg'" class="space-y-2">
|
||||
<Label for="otg-udc">{{ t('settings.otgUdc') }}</Label>
|
||||
<NativeSelect id="otg-udc" v-model="config.hid_otg_udc" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.autoRecommended') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="udc in devices.udc" :key="udc.name" :value="udc.name">{{ udc.name }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="config.hid_otg_udc"
|
||||
@update:model-value="value => config.hid_otg_udc = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="otg-udc" class="w-full"><SelectValue :placeholder="t('settings.autoRecommended')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.autoRecommended') }}</SelectItem>
|
||||
<SelectItem v-for="udc in devices.udc" :key="udc.name" :value="udc.name">{{ udc.name }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<template v-if="config.hid_backend === 'ch9329'">
|
||||
@@ -3343,30 +3374,37 @@ watch(isWindows, () => {
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="otg-network-interface">{{ t('settings.otgNetworkInterface') }}</Label>
|
||||
<NativeSelect
|
||||
id="otg-network-interface"
|
||||
v-model="config.otg_network_interface"
|
||||
class="w-full"
|
||||
<Select
|
||||
:model-value="config.otg_network_interface"
|
||||
:disabled="otgNetworkInterfaces.length === 0"
|
||||
:aria-invalid="Boolean(otgNetworkInterfacesError)"
|
||||
@update:model-value="value => config.otg_network_interface = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<NativeSelectOption
|
||||
v-if="otgNetworkInterfacesError"
|
||||
:value="config.otg_network_interface"
|
||||
<SelectTrigger
|
||||
id="otg-network-interface"
|
||||
class="w-full"
|
||||
:aria-invalid="Boolean(otgNetworkInterfacesError)"
|
||||
>
|
||||
{{ otgNetworkInterfacesError }}
|
||||
</NativeSelectOption>
|
||||
<NativeSelectOption v-else-if="otgNetworkInterfaces.length === 0" value="">
|
||||
{{ t('settings.otgNetworkNone') }}
|
||||
</NativeSelectOption>
|
||||
<NativeSelectOption
|
||||
v-for="item in otgNetworkInterfaces"
|
||||
:key="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
{{ item.name }} · {{ item.interface_type }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<SelectValue :placeholder="otgNetworkInterfacesError || t('settings.otgNetworkNone')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-if="otgNetworkInterfacesError"
|
||||
:value="config.otg_network_interface || EMPTY_SELECT_VALUE"
|
||||
>
|
||||
{{ otgNetworkInterfacesError }}
|
||||
</SelectItem>
|
||||
<SelectItem v-else-if="otgNetworkInterfaces.length === 0" :value="EMPTY_SELECT_VALUE">
|
||||
{{ t('settings.otgNetworkNone') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-for="item in otgNetworkInterfaces"
|
||||
:key="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
{{ item.name }} · {{ item.interface_type }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -4058,17 +4096,23 @@ watch(isWindows, () => {
|
||||
<div v-if="['usbrelay', 'serial'].includes(atxConfig.driver)" class="grid gap-4 sm:grid-cols-2">
|
||||
<div v-if="['usbrelay', 'serial'].includes(atxConfig.driver)" class="space-y-2">
|
||||
<Label for="atx-device">{{ t('settings.atxDevice') }}</Label>
|
||||
<NativeSelect id="atx-device" v-model="atxConfig.device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.selectDevice') }}</NativeSelectOption>
|
||||
<NativeSelectOption
|
||||
v-for="dev in getAtxDevicesForDriver(atxConfig.driver)"
|
||||
:key="dev"
|
||||
:value="dev"
|
||||
:disabled="atxConfig.driver === 'serial' && isAtxSerialDeviceReserved(dev)"
|
||||
>
|
||||
{{ formatAtxDeviceLabel(atxConfig.driver, dev) }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="atxConfig.device"
|
||||
@update:model-value="value => atxConfig.device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="atx-device" class="w-full"><SelectValue :placeholder="t('settings.selectDevice')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.selectDevice') }}</SelectItem>
|
||||
<SelectItem
|
||||
v-for="dev in getAtxDevicesForDriver(atxConfig.driver)"
|
||||
:key="dev"
|
||||
:value="dev"
|
||||
:disabled="atxConfig.driver === 'serial' && isAtxSerialDeviceReserved(dev)"
|
||||
>
|
||||
{{ formatAtxDeviceLabel(atxConfig.driver, dev) }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div v-if="atxConfig.driver === 'serial'" class="space-y-2">
|
||||
<Label for="atx-baudrate">{{ t('settings.baudRate') }}</Label>
|
||||
@@ -4089,10 +4133,16 @@ watch(isWindows, () => {
|
||||
<div class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="space-y-2">
|
||||
<Label for="power-device">{{ t('settings.atxGpioChip') }}</Label>
|
||||
<NativeSelect id="power-device" v-model="atxConfig.power.device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.atxDriverNone') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="atxConfig.power.device"
|
||||
@update:model-value="value => atxConfig.power.device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="power-device" class="w-full"><SelectValue :placeholder="t('settings.atxDriverNone')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.atxDriverNone') }}</SelectItem>
|
||||
<SelectItem v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="power-pin">{{ t('settings.atxPin') }}</Label>
|
||||
@@ -4113,10 +4163,16 @@ watch(isWindows, () => {
|
||||
<div class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="space-y-2">
|
||||
<Label for="reset-device">{{ t('settings.atxGpioChip') }}</Label>
|
||||
<NativeSelect id="reset-device" v-model="atxConfig.reset.device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.atxDriverNone') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="atxConfig.reset.device"
|
||||
@update:model-value="value => atxConfig.reset.device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="reset-device" class="w-full"><SelectValue :placeholder="t('settings.atxDriverNone')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.atxDriverNone') }}</SelectItem>
|
||||
<SelectItem v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="reset-pin">{{ t('settings.atxPin') }}</Label>
|
||||
@@ -4137,10 +4193,16 @@ watch(isWindows, () => {
|
||||
<div class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="space-y-2">
|
||||
<Label for="led-device">{{ t('settings.atxGpioChip') }}</Label>
|
||||
<NativeSelect id="led-device" v-model="atxConfig.led.device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.atxDriverNone') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="atxConfig.led.device"
|
||||
@update:model-value="value => atxConfig.led.device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="led-device" class="w-full"><SelectValue :placeholder="t('settings.atxDriverNone')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.atxDriverNone') }}</SelectItem>
|
||||
<SelectItem v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="led-pin">{{ t('settings.atxPin') }}</Label>
|
||||
@@ -4161,10 +4223,16 @@ watch(isWindows, () => {
|
||||
<div class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="space-y-2">
|
||||
<Label for="hdd-device">{{ t('settings.atxGpioChip') }}</Label>
|
||||
<NativeSelect id="hdd-device" v-model="atxConfig.hdd.device" class="w-full">
|
||||
<NativeSelectOption value="">{{ t('settings.atxDriverNone') }}</NativeSelectOption>
|
||||
<NativeSelectOption v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<Select
|
||||
:model-value="atxConfig.hdd.device"
|
||||
@update:model-value="value => atxConfig.hdd.device = value === EMPTY_SELECT_VALUE ? '' : String(value)"
|
||||
>
|
||||
<SelectTrigger id="hdd-device" class="w-full"><SelectValue :placeholder="t('settings.atxDriverNone')" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="EMPTY_SELECT_VALUE">{{ t('settings.atxDriverNone') }}</SelectItem>
|
||||
<SelectItem v-for="dev in atxDevices.gpio_chips" :key="dev" :value="dev">{{ dev }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="hdd-pin">{{ t('settings.atxPin') }}</Label>
|
||||
|
||||
Reference in New Issue
Block a user