From e578e473d5fd70c0f524e6b969bc5f327a367783 Mon Sep 17 00:00:00 2001 From: StarsSail <152023695+wysha-object@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:53:51 +0800 Subject: [PATCH] fix: prevent input focus loss in allowOrigins (#7397) * fix: update CORS configuration to use key-value pairs for allowed origins * fix: refactor CORS configuration to use AllowOriginItem interface for better type safety --- .../setting/mods/external-controller-cors.tsx | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/components/setting/mods/external-controller-cors.tsx b/src/components/setting/mods/external-controller-cors.tsx index c3dcbda7f..01696f6af 100644 --- a/src/components/setting/mods/external-controller-cors.tsx +++ b/src/components/setting/mods/external-controller-cors.tsx @@ -1,7 +1,7 @@ import { Delete as DeleteIcon } from '@mui/icons-material' import { Box, Button, Divider, List, ListItem, TextField } from '@mui/material' import { useLockFn, useRequest } from 'ahooks' -import { forwardRef, useImperativeHandle, useMemo, useState } from 'react' +import { forwardRef, useImperativeHandle, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { BaseDialog, Switch } from '@/components/base' @@ -72,29 +72,39 @@ interface ClashHeaderConfigingRef { close: () => void } +interface AllowOriginItem { + key: number + value: string +} + export const HeaderConfiguration = forwardRef( (props, ref) => { const { t } = useTranslation() const { clash, mutateClash, patchClash } = useClash() const [open, setOpen] = useState(false) + const lastKeyRef = useRef(0) // 用于生成唯一的key + // CORS配置状态管理 const [corsConfig, setCorsConfig] = useState<{ allowPrivateNetwork: boolean - allowOrigins: string[] + allowOrigins: AllowOriginItem[] }>(() => { const cors = clash?.['external-controller-cors'] const origins = cors?.['allow-origins'] ?? [] return { allowPrivateNetwork: cors?.['allow-private-network'] ?? true, - allowOrigins: filterBaseOriginsForUI(origins), + allowOrigins: filterBaseOriginsForUI(origins).map((origin) => { + lastKeyRef.current += 1 + return { key: lastKeyRef.current, value: origin } + }), } }) // 处理CORS配置变更 const handleCorsConfigChange = ( key: 'allowPrivateNetwork' | 'allowOrigins', - value: boolean | string[], + value: boolean | AllowOriginItem[], ) => { setCorsConfig((prev) => ({ ...prev, @@ -104,13 +114,17 @@ export const HeaderConfiguration = forwardRef( // 添加新的允许来源 const handleAddOrigin = () => { - handleCorsConfigChange('allowOrigins', [...corsConfig.allowOrigins, '']) + lastKeyRef.current += 1 + handleCorsConfigChange('allowOrigins', [ + ...corsConfig.allowOrigins, + { key: lastKeyRef.current, value: '' }, + ]) } // 更新允许来源列表中的某一项 const handleUpdateOrigin = (index: number, value: string) => { const newOrigins = [...corsConfig.allowOrigins] - newOrigins[index] = value + newOrigins[index] = { ...newOrigins[index], value } handleCorsConfigChange('allowOrigins', newOrigins) } @@ -125,7 +139,9 @@ export const HeaderConfiguration = forwardRef( const { loading, run: saveConfig } = useRequest( async () => { // 保存时使用完整的源列表(包括开发URL) - const fullOrigins = getFullOrigins(corsConfig.allowOrigins) + const fullOrigins = getFullOrigins( + corsConfig.allowOrigins.map((origin) => origin.value), + ) await patchClash({ 'external-controller-cors': { @@ -154,9 +170,13 @@ export const HeaderConfiguration = forwardRef( open: () => { const cors = clash?.['external-controller-cors'] const origins = cors?.['allow-origins'] ?? [] + lastKeyRef.current = 0 setCorsConfig({ allowPrivateNetwork: cors?.['allow-private-network'] ?? true, - allowOrigins: filterBaseOriginsForUI(origins), + allowOrigins: filterBaseOriginsForUI(origins).map((origin) => { + lastKeyRef.current += 1 + return { key: lastKeyRef.current, value: origin } + }), }) setOpen(true) }, @@ -167,19 +187,6 @@ export const HeaderConfiguration = forwardRef( await saveConfig() }) - const originEntries = useMemo(() => { - const counts: Record = {} - return corsConfig.allowOrigins.map((origin, index) => { - const occurrence = (counts[origin] = (counts[origin] ?? 0) + 1) - const keyBase = origin || 'origin' - return { - origin, - index, - key: `${keyBase}-${occurrence}`, - } - }) - }, [corsConfig.allowOrigins]) - return ( (
{t('settings.sections.externalCors.fields.allowedOrigins')}
- {originEntries.map(({ origin, index, key }) => ( + {corsConfig.allowOrigins.map(({ key, value: origin }, index) => (