fix: improve document parsing settings spacing
This commit is contained in:
@@ -558,15 +558,15 @@ describe('DocumentParsingSettingsSection', () => {
|
||||
fireEvent.click(screen.getByText('HTTP 高级设置'))
|
||||
expect(screen.getByRole('switch', { name: '保留页眉文字' })).not.toBeChecked()
|
||||
expect(screen.getByRole('switch', { name: '保留text' })).not.toBeChecked()
|
||||
fireEvent.click(screen.getByRole('button', { name: '服务默认' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '全部保留' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '自定义' }))
|
||||
expect(screen.getByRole('switch', { name: '保留text' })).not.toBeChecked()
|
||||
fireEvent.click(screen.getByRole('switch', { name: '保留页眉文字' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '本地模型' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '远程服务' }))
|
||||
expect(screen.getByRole('switch', { name: '保留text' })).not.toBeChecked()
|
||||
fireEvent.click(screen.getByRole('button', { name: '恢复默认' }))
|
||||
expect(screen.getByRole('switch', { name: '保留页眉文字' })).toBeChecked()
|
||||
expect(screen.queryByRole('switch', { name: '保留text' })).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: '测试解析' })).toBeDisabled()
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
||||
await waitFor(() => expect(update).toHaveBeenCalledWith(expect.objectContaining({ httpOcr: expect.objectContaining({ filterMode: 'custom', ignoredLabels: ['text'] }) })))
|
||||
await waitFor(() => expect(update).toHaveBeenCalledWith(expect.objectContaining({ httpOcr: expect.objectContaining({ filterMode: 'default', ignoredLabels: [] }) })))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import type { defaultHttpOcrSettings } from '../../shared/document-parsing-contracts'
|
||||
import { SegmentedControl } from './WorkspacePrimitives'
|
||||
import { defaultHttpOcrSettings } from '../../shared/document-parsing-contracts'
|
||||
|
||||
type HttpSettings = typeof defaultHttpOcrSettings
|
||||
const booleanOptions = {
|
||||
@@ -29,7 +28,7 @@ export function HttpOcrSettings({ value, onChange, dirty, credentialConfigured,
|
||||
const [checking, setChecking] = useState(false)
|
||||
const [check, setCheck] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
return <section className="settings-section">
|
||||
return <section className="settings-section http-ocr-settings">
|
||||
<strong>HTTP PaddleOCR-VL</strong>
|
||||
<label className="field"><span>服务地址</span><input type="url" value={value.baseUrl}
|
||||
onChange={(event) => onChange({ ...value, baseUrl: event.target.value })} /></label>
|
||||
@@ -56,17 +55,20 @@ export function HttpOcrSettings({ value, onChange, dirty, credentialConfigured,
|
||||
{check && <p role="status">{check}</p>}{error && <p role="alert">{error}</p>}
|
||||
<details><summary>HTTP 高级设置</summary>
|
||||
<p>高级能力取决于服务部署;接口声明不代表模型已安装或效果已经实测。</p>
|
||||
<SegmentedControl ariaLabel="内容过滤" value={value.filterMode}
|
||||
options={[{ value: 'default', label: '服务默认' }, { value: 'all', label: '全部保留' }, { value: 'custom', label: '自定义' }]}
|
||||
onChange={(filterMode) => onChange({ ...value, filterMode })} />
|
||||
{value.filterMode === 'custom' && <>
|
||||
<small>未设置自定义排除项时,从全部保留开始。</small>
|
||||
<div className="document-parsing-control-header">
|
||||
<strong>内容过滤</strong>
|
||||
<button type="button" className="secondary-button" onClick={() => onChange({
|
||||
...value, filterMode: defaultHttpOcrSettings.filterMode, ignoredLabels: []
|
||||
})}>恢复默认</button>
|
||||
</div>
|
||||
<small>直接选择需要保留的内容;修改后将使用自定义过滤设置。</small>
|
||||
<div className="document-parsing-filter-list">
|
||||
{[...new Set([...Object.keys(labels), ...value.ignoredLabels])].map((key) => <label className="toggle-row" key={key}>
|
||||
<span>保留{labels[key] ?? key}</span><input type="checkbox" role="switch" checked={!value.ignoredLabels.includes(key)}
|
||||
onChange={(event) => onChange({ ...value, ignoredLabels: event.target.checked
|
||||
onChange={(event) => onChange({ ...value, filterMode: 'custom', ignoredLabels: event.target.checked
|
||||
? value.ignoredLabels.filter((label) => label !== key) : [...value.ignoredLabels, key] })} />
|
||||
</label>)}
|
||||
</>}
|
||||
</div>
|
||||
<div className="document-parsing-grid">
|
||||
{Object.entries(booleanOptions).map(([key, label]) => <label className="field" key={key}><span>{label}</span>
|
||||
<select value={String(value.options[key as keyof typeof booleanOptions] ?? 'default')}
|
||||
@@ -80,7 +82,7 @@ export function HttpOcrSettings({ value, onChange, dirty, credentialConfigured,
|
||||
<label className="field"><span>HTTP 请求超时(秒)</span><input type="number" min={10} max={300} value={value.timeoutSeconds}
|
||||
onChange={(event) => onChange({ ...value, timeoutSeconds: Number(event.target.value) })} /></label>
|
||||
</div>
|
||||
<details><summary>版面与生成参数</summary><p>留空使用服务默认;接口字段的实际边界与效果需用当前部署测试。</p>
|
||||
<details className="document-parsing-subsection"><summary>版面与生成参数</summary><p>留空使用服务默认;接口字段的实际边界与效果需用当前部署测试。</p>
|
||||
<div className="document-parsing-grid">
|
||||
{Object.entries(numericOptions).map(([key, label]) => <label className="field" key={key}><span>{label}</span>
|
||||
<input type="number" step="any" value={value.options[key as keyof typeof numericOptions] ?? ''} onChange={(event) => {
|
||||
|
||||
@@ -10827,7 +10827,53 @@ details.settings-section > :not(summary) + :not(summary) {
|
||||
.document-parsing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.document-parsing-control-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.document-parsing-control-header strong {
|
||||
color: var(--text-primary);
|
||||
font-size: var(--font-caption);
|
||||
}
|
||||
|
||||
.document-parsing-control-header .secondary-button {
|
||||
min-height: 30px;
|
||||
padding: 0 var(--space-3);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.document-parsing-filter-list {
|
||||
display: grid;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.document-parsing-subsection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-2);
|
||||
padding-top: var(--space-3);
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.document-parsing-subsection > summary {
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
font-size: var(--font-caption);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.document-parsing-subsection > p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-caption);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.document-parsing-grid .field > small {
|
||||
|
||||
Reference in New Issue
Block a user