diff --git a/web/src/components/auth/LoginForm.jsx b/web/src/components/auth/LoginForm.jsx index 7e8c0ce017f1..3e25f38ec7bf 100644 --- a/web/src/components/auth/LoginForm.jsx +++ b/web/src/components/auth/LoginForm.jsx @@ -663,6 +663,7 @@ const LoginForm = () => { setAgreedToTerms(e.target.checked)} + name='components-auth-loginform-checkbox-1' > {t('我已阅读并同意')} @@ -750,6 +751,7 @@ const LoginForm = () => { label={t('用户名或邮箱')} placeholder={t('请输入您的用户名或邮箱地址')} name='username' + autoComplete='username' onChange={(value) => handleChange('username', value)} prefix={} /> @@ -760,6 +762,7 @@ const LoginForm = () => { placeholder={t('请输入您的密码')} name='password' mode='password' + autoComplete='current-password' onChange={(value) => handleChange('password', value)} prefix={} /> @@ -769,6 +772,7 @@ const LoginForm = () => { setAgreedToTerms(e.target.checked)} + name='components-auth-loginform-checkbox-2' > {t('我已阅读并同意')} diff --git a/web/src/components/auth/RegisterForm.jsx b/web/src/components/auth/RegisterForm.jsx index 0a755b194431..98175e6d582e 100644 --- a/web/src/components/auth/RegisterForm.jsx +++ b/web/src/components/auth/RegisterForm.jsx @@ -578,6 +578,7 @@ const RegisterForm = () => { label={t('用户名')} placeholder={t('请输入用户名')} name='username' + autoComplete='username' onChange={(value) => handleChange('username', value)} prefix={} /> @@ -588,6 +589,7 @@ const RegisterForm = () => { placeholder={t('输入密码,最短 8 位,最长 20 位')} name='password' mode='password' + autoComplete='new-password' onChange={(value) => handleChange('password', value)} prefix={} /> @@ -598,6 +600,7 @@ const RegisterForm = () => { placeholder={t('确认密码')} name='password2' mode='password' + autoComplete='new-password' onChange={(value) => handleChange('password2', value)} prefix={} /> @@ -610,6 +613,7 @@ const RegisterForm = () => { placeholder={t('输入邮箱地址')} name='email' type='email' + autoComplete='email' onChange={(value) => handleChange('email', value)} prefix={} suffix={ @@ -629,6 +633,7 @@ const RegisterForm = () => { label={t('验证码')} placeholder={t('输入验证码')} name='verification_code' + autoComplete='one-time-code' onChange={(value) => handleChange('verification_code', value) } @@ -642,6 +647,7 @@ const RegisterForm = () => { setAgreedToTerms(e.target.checked)} + name='components-auth-registerform-checkbox-1' > {t('我已阅读并同意')} diff --git a/web/src/components/common/DocumentRenderer/index.jsx b/web/src/components/common/DocumentRenderer/index.jsx index 68e868c51d8a..b04fde0d27ed 100644 --- a/web/src/components/common/DocumentRenderer/index.jsx +++ b/web/src/components/common/DocumentRenderer/index.jsx @@ -72,7 +72,13 @@ const sanitizeHtml = (html) => { * @param {string} cacheKey - 本地存储缓存键 * @param {string} emptyMessage - 空内容时的提示消息 */ -const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { +const DocumentRenderer = ({ + apiEndpoint, + title, + cacheKey, + emptyMessage, + enabled = true, +}) => { const { t } = useTranslation(); const [content, setContent] = useState(''); const [loading, setLoading] = useState(true); @@ -80,6 +86,14 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { const [processedHtmlContent, setProcessedHtmlContent] = useState(''); const loadContent = async () => { + if (!enabled) { + setContent(''); + setProcessedHtmlContent(''); + setHtmlStyles(''); + setLoading(false); + return; + } + // 先从缓存中获取 const cachedContent = localStorage.getItem(cacheKey) || ''; if (cachedContent) { @@ -124,7 +138,7 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { useEffect(() => { loadContent(); - }, []); + }, [enabled]); // 处理HTML样式注入 useEffect(() => { diff --git a/web/src/components/common/modals/RiskAcknowledgementModal.jsx b/web/src/components/common/modals/RiskAcknowledgementModal.jsx index 54aa62eaaceb..4e1ec45ec4ca 100644 --- a/web/src/components/common/modals/RiskAcknowledgementModal.jsx +++ b/web/src/components/common/modals/RiskAcknowledgementModal.jsx @@ -169,6 +169,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ onChange={(event) => { handleChecklistChange(index, event.target.checked); }} + name='components-common-modals-riskacknowledgementmodal-checkbox-1' > {item} @@ -198,6 +199,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ onCut={(event) => event.preventDefault()} onPaste={(event) => event.preventDefault()} onDrop={(event) => event.preventDefault()} + name='components-common-modals-riskacknowledgementmodal-input-1' /> {!typedMatched && typedText ? ( diff --git a/web/src/components/common/modals/SecureVerificationModal.jsx b/web/src/components/common/modals/SecureVerificationModal.jsx index 6c61c291d1d6..eac45661635c 100644 --- a/web/src/components/common/modals/SecureVerificationModal.jsx +++ b/web/src/components/common/modals/SecureVerificationModal.jsx @@ -191,6 +191,7 @@ const SecureVerificationModal = ({ } style={{ width: '100%' }} + name='components-common-modals-secureverificationmodal-input-1' /> diff --git a/web/src/components/common/ui/JSONEditor.jsx b/web/src/components/common/ui/JSONEditor.jsx index 21530db3908e..6dd9850d4dd1 100644 --- a/web/src/components/common/ui/JSONEditor.jsx +++ b/web/src/components/common/ui/JSONEditor.jsx @@ -345,6 +345,7 @@ const JSONEditor = ({ updateValue(pairId, newValue)} + id='components-common-ui-jsoneditor-switch-1' /> {value ? t('true') : t('false')} @@ -360,6 +361,7 @@ const JSONEditor = ({ onChange={(newValue) => updateValue(pairId, newValue)} style={{ width: '100%' }} placeholder={t('输入数字')} + name='components-common-ui-jsoneditor-inputnumber-1' /> ); } @@ -379,6 +381,7 @@ const JSONEditor = ({ } }} placeholder={t('输入JSON对象')} + name='components-common-ui-jsoneditor-textarea-1' /> ); } @@ -402,6 +405,7 @@ const JSONEditor = ({ } updateValue(pairId, convertedValue); }} + name='components-common-ui-jsoneditor-input-1' /> ); }; @@ -452,6 +456,7 @@ const JSONEditor = ({ value={pair.key} onChange={(newKey) => updateKey(pair.id, newKey)} status={isDuplicate ? 'warning' : undefined} + name='components-common-ui-jsoneditor-input-2' /> {isDuplicate && ( @@ -565,6 +571,7 @@ const JSONEditor = ({ value={pair.key} onChange={(newKey) => updateKey(pair.id, newKey)} status={isDuplicate ? 'warning' : undefined} + name='components-common-ui-jsoneditor-input-4' /> {isDuplicate && ( @@ -581,6 +588,7 @@ const JSONEditor = ({ placeholder={t('区域')} value={pair.value} onChange={(newValue) => updateValue(pair.id, newValue)} + name='components-common-ui-jsoneditor-input-5' /> @@ -627,7 +635,12 @@ const JSONEditor = ({ const hasJsonError = jsonError && jsonError.trim() !== ''; return ( - +
+
+
+ {label} +
+
@@ -676,6 +689,7 @@ const JSONEditor = ({ value={value} rules={rules} style={{ display: 'none' }} + name={field} noLabel={true} {...props} /> @@ -688,6 +702,7 @@ const JSONEditor = ({ onChange={handleManualChange} showClear={showClear} rows={Math.max(8, manualText ? manualText.split('\n').length : 8)} + name='components-common-ui-jsoneditor-textarea-2' /> {/* 隐藏的Form字段用于验证和数据绑定 */} @@ -711,7 +727,7 @@ const JSONEditor = ({ )} {extraFooter &&
{extraFooter}
}
- +
); }; diff --git a/web/src/components/common/ui/SelectableButtonGroup.jsx b/web/src/components/common/ui/SelectableButtonGroup.jsx index dc5c428b5164..c95aae74cb4d 100644 --- a/web/src/components/common/ui/SelectableButtonGroup.jsx +++ b/web/src/components/common/ui/SelectableButtonGroup.jsx @@ -198,6 +198,7 @@ const SelectableButtonGroup = ({ checked={isActive} onChange={() => onChange(item.value)} style={{ pointerEvents: 'auto' }} + name='components-common-ui-selectablebuttongroup-checkbox-1' /> } style={{ width: '100%', cursor: 'default' }} diff --git a/web/src/components/layout/PageLayout.jsx b/web/src/components/layout/PageLayout.jsx index 51666b5ef392..9336724e9856 100644 --- a/web/src/components/layout/PageLayout.jsx +++ b/web/src/components/layout/PageLayout.jsx @@ -24,6 +24,7 @@ import App from '../../App'; import FooterBar from './Footer'; import { ToastContainer } from 'react-toastify'; import React, { useContext, useEffect, useState } from 'react'; +import useFormFieldA11yPatch from '../../hooks/common/useFormFieldA11yPatch'; import { useIsMobile } from '../../hooks/common/useIsMobile'; import { useSidebarCollapsed } from '../../hooks/common/useSidebarCollapsed'; import { useTranslation } from 'react-i18next'; @@ -143,6 +144,8 @@ const PageLayout = () => { } }, [i18n, userState?.user?.setting]); + useFormFieldA11yPatch(location.pathname); + return ( { @@ -102,6 +103,31 @@ const CustomInputRender = (props) => { }; }, [handlePaste]); + useRepeatingDomPatch(() => { + const patchInputs = () => { + const container = containerRef.current; + if (!container) return; + + const visibleTextarea = container.querySelector( + '.flex-1 textarea:not([aria-hidden="true"])', + ); + if (visibleTextarea) { + visibleTextarea.name = 'playground-message-input'; + visibleTextarea.id = 'playground-message-input'; + visibleTextarea.setAttribute('data-playground-input', 'true'); + } + + const hiddenTextarea = container.querySelector( + 'textarea[aria-hidden="true"]', + ); + if (hiddenTextarea) { + hiddenTextarea.name = 'playground-message-shadow'; + } + }; + + patchInputs(); + }, []); + // 清空按钮 const styledClearNode = clearContextNode ? React.cloneElement(clearContextNode, { diff --git a/web/src/components/playground/CustomRequestEditor.jsx b/web/src/components/playground/CustomRequestEditor.jsx index 68f0ae87195a..26ef66b26c9c 100644 --- a/web/src/components/playground/CustomRequestEditor.jsx +++ b/web/src/components/playground/CustomRequestEditor.jsx @@ -127,6 +127,8 @@ const CustomRequestEditor = ({