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
This commit is contained in:
StarsSail
2026-07-03 17:53:51 +00:00
committed by GitHub
parent dd3ef698f5
commit e578e473d5
@@ -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<ClashHeaderConfigingRef>(
(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<ClashHeaderConfigingRef>(
// 添加新的允许来源
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<ClashHeaderConfigingRef>(
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<ClashHeaderConfigingRef>(
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<ClashHeaderConfigingRef>(
await saveConfig()
})
const originEntries = useMemo(() => {
const counts: Record<string, number> = {}
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 (
<BaseDialog
open={open}
@@ -224,7 +231,7 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
<div style={{ marginBottom: 8, fontWeight: 'bold' }}>
{t('settings.sections.externalCors.fields.allowedOrigins')}
</div>
{originEntries.map(({ origin, index, key }) => (
{corsConfig.allowOrigins.map(({ key, value: origin }, index) => (
<div
key={key}
style={{