fix(ui): fix bug on processor page

This commit is contained in:
shiyu
2025-09-20 14:16:06 +08:00
parent 5a29c579dc
commit 45d63febb9
7 changed files with 97 additions and 77 deletions
+1
View File
@@ -62,6 +62,7 @@ const ProfileModal = memo(function ProfileModal({ open, onClose }: ProfileModalP
confirmLoading={loading} confirmLoading={loading}
okText={t('Save')} okText={t('Save')}
cancelText={t('Cancel')} cancelText={t('Cancel')}
forceRender
> >
<Form form={form} layout="vertical"> <Form form={form} layout="vertical">
<Form.Item name="username" label={t('Username')}> <Form.Item name="username" label={t('Username')}>
+1 -1
View File
@@ -223,7 +223,7 @@ const AdaptersPage = memo(function AdaptersPage() {
width={480} width={480}
open={open} open={open}
onClose={() => { setOpen(false); setEditing(null); }} onClose={() => { setOpen(false); setEditing(null); }}
destroyOnClose destroyOnHidden
extra={ extra={
<Space> <Space>
<Button onClick={() => { setOpen(false); setEditing(null); }}>{t('Cancel')}</Button> <Button onClick={() => { setOpen(false); setEditing(null); }}>{t('Cancel')}</Button>
@@ -29,7 +29,7 @@ export const CreateDirModal: React.FC<CreateDirModalProps> = ({ open, onOk, onCa
onOk={handleOk} onOk={handleOk}
onCancel={onCancel} onCancel={onCancel}
okButtonProps={{ disabled: !name.trim() }} okButtonProps={{ disabled: !name.trim() }}
destroyOnClose destroyOnHidden
> >
<Input <Input
placeholder={t('Folder Name')} placeholder={t('Folder Name')}
@@ -58,7 +58,7 @@ export const ProcessorModal: React.FC<ProcessorModalProps> = (props) => {
onCancel={onCancel} onCancel={onCancel}
onOk={onOk} onOk={onOk}
confirmLoading={loading} confirmLoading={loading}
destroyOnClose destroyOnHidden
> >
<Form form={form} layout="vertical" onValuesChange={handleFormValuesChange}> <Form form={form} layout="vertical" onValuesChange={handleFormValuesChange}>
<Form.Item name="processor_type" label={t('Processor')} required> <Form.Item name="processor_type" label={t('Processor')} required>
@@ -32,7 +32,7 @@ export const RenameModal: React.FC<RenameModalProps> = ({ entry, onOk, onCancel
onOk={handleOk} onOk={handleOk}
onCancel={onCancel} onCancel={onCancel}
okButtonProps={{ disabled: !name.trim() || name.trim() === entry?.name }} okButtonProps={{ disabled: !name.trim() || name.trim() === entry?.name }}
destroyOnClose destroyOnHidden
> >
<Input <Input
placeholder={t('New Name')} placeholder={t('New Name')}
+91 -72
View File
@@ -17,7 +17,6 @@ import {
theme, theme,
} from 'antd'; } from 'antd';
import Editor from '@monaco-editor/react'; import Editor from '@monaco-editor/react';
import PageCard from '../components/PageCard';
import { ProcessorConfigForm } from '../components/ProcessorConfigForm'; import { ProcessorConfigForm } from '../components/ProcessorConfigForm';
import PathSelectorModal, { type PathSelectorMode } from '../components/PathSelectorModal'; import PathSelectorModal, { type PathSelectorMode } from '../components/PathSelectorModal';
import { processorsApi, type ProcessorTypeMeta } from '../api/processors'; import { processorsApi, type ProcessorTypeMeta } from '../api/processors';
@@ -30,6 +29,7 @@ type TabKey = 'editor' | 'runner';
const ProcessorsPage = memo(function ProcessorsPage() { const ProcessorsPage = memo(function ProcessorsPage() {
const { t } = useI18n(); const { t } = useI18n();
const { token } = theme.useToken(); const { token } = theme.useToken();
const [messageApi, contextHolder] = message.useMessage();
const [processors, setProcessors] = useState<ProcessorTypeMeta[]>([]); const [processors, setProcessors] = useState<ProcessorTypeMeta[]>([]);
const [loadingList, setLoadingList] = useState(false); const [loadingList, setLoadingList] = useState(false);
const [selectedType, setSelectedType] = useState<string>(''); const [selectedType, setSelectedType] = useState<string>('');
@@ -60,11 +60,11 @@ const ProcessorsPage = memo(function ProcessorsPage() {
const list = await processorsApi.list(); const list = await processorsApi.list();
setProcessors(list); setProcessors(list);
} catch (err: any) { } catch (err: any) {
message.error(err?.message || t('Load failed')); messageApi.error(err?.message || t('Load failed'));
} finally { } finally {
setLoadingList(false); setLoadingList(false);
} }
}, [t]); }, [messageApi, t]);
useEffect(() => { useEffect(() => {
loadList(); loadList();
@@ -103,7 +103,7 @@ const ProcessorsPage = memo(function ProcessorsPage() {
}) })
.catch((err: any) => { .catch((err: any) => {
if (controller.signal.aborted) return; if (controller.signal.aborted) return;
message.error(err?.message || t('Load failed')); messageApi.error(err?.message || t('Load failed'));
setSource(''); setSource('');
setInitialSource(''); setInitialSource('');
setModulePath(''); setModulePath('');
@@ -114,19 +114,24 @@ const ProcessorsPage = memo(function ProcessorsPage() {
} }
}); });
return () => controller.abort(); return () => controller.abort();
}, [selectedType, t]); }, [messageApi, selectedType, t]);
useEffect(() => { useEffect(() => {
if (!selectedProcessorMeta) {
form.resetFields();
setIsDirectory(false);
return;
}
form.resetFields(); form.resetFields();
const defaults: Record<string, any> = {}; const defaults: Record<string, any> = {};
selectedProcessorMeta?.config_schema?.forEach(field => { selectedProcessorMeta.config_schema?.forEach(field => {
if (field.default !== undefined) { if (field.default !== undefined) {
defaults[field.key] = field.default; defaults[field.key] = field.default;
} }
}); });
form.setFieldsValue({ form.setFieldsValue({
path: '', path: '',
overwrite: !!selectedProcessorMeta?.produces_file, overwrite: !!selectedProcessorMeta.produces_file,
save_to: undefined, save_to: undefined,
config: defaults, config: defaults,
}); });
@@ -172,26 +177,26 @@ const ProcessorsPage = memo(function ProcessorsPage() {
setSavingSource(true); setSavingSource(true);
await processorsApi.updateSource(selectedType, source); await processorsApi.updateSource(selectedType, source);
setInitialSource(source); setInitialSource(source);
message.success(t('Source saved')); messageApi.success(t('Source saved'));
} catch (err: any) { } catch (err: any) {
message.error(err?.message || t('Operation failed')); messageApi.error(err?.message || t('Operation failed'));
} finally { } finally {
setSavingSource(false); setSavingSource(false);
} }
}, [selectedType, source, t]); }, [messageApi, selectedType, source, t]);
const handleReloadProcessors = useCallback(async () => { const handleReloadProcessors = useCallback(async () => {
try { try {
setReloading(true); setReloading(true);
await processorsApi.reload(); await processorsApi.reload();
message.success(t('Processors reloaded')); messageApi.success(t('Processors reloaded'));
await loadList(); await loadList();
} catch (err: any) { } catch (err: any) {
message.error(err?.message || t('Operation failed')); messageApi.error(err?.message || t('Operation failed'));
} finally { } finally {
setReloading(false); setReloading(false);
} }
}, [loadList, t]); }, [loadList, messageApi, t]);
const openPathSelector = useCallback((field: 'path' | 'save_to', mode: PathSelectorMode) => { const openPathSelector = useCallback((field: 'path' | 'save_to', mode: PathSelectorMode) => {
setPathModalField(field); setPathModalField(field);
@@ -211,7 +216,7 @@ const ProcessorsPage = memo(function ProcessorsPage() {
const handleRun = useCallback(async () => { const handleRun = useCallback(async () => {
if (!selectedType) { if (!selectedType) {
message.warning(t('Please select a processor')); messageApi.warning(t('Please select a processor'));
return; return;
} }
try { try {
@@ -237,20 +242,20 @@ const ProcessorsPage = memo(function ProcessorsPage() {
payload.save_to = values.save_to; payload.save_to = values.save_to;
} }
const resp = await processorsApi.process(payload); const resp = await processorsApi.process(payload);
message.success(`${t('Task submitted')}: ${resp.task_id}`); messageApi.success(`${t('Task submitted')}: ${resp.task_id}`);
} catch (err: any) { } catch (err: any) {
if (err?.errorFields) { if (err?.errorFields) {
return; return;
} }
message.error(err?.message || t('Operation failed')); messageApi.error(err?.message || t('Operation failed'));
} finally { } finally {
setRunning(false); setRunning(false);
} }
}, [form, selectedProcessorMeta, selectedType, t]); }, [form, messageApi, selectedProcessorMeta, selectedType, t]);
const selectedConfigPath = pathModalField === 'path' const selectedConfigPath = pathModalField === 'path'
? form.getFieldValue('path') || '/' ? (selectedType ? form.getFieldValue('path') : undefined) || '/'
: form.getFieldValue('save_to') || '/'; : (selectedType ? form.getFieldValue('save_to') : undefined) || '/';
const renderProcessorList = () => { const renderProcessorList = () => {
if (loadingList) { if (loadingList) {
@@ -369,66 +374,80 @@ const ProcessorsPage = memo(function ProcessorsPage() {
{ {
key: 'runner', key: 'runner',
label: t('Run Processor'), label: t('Run Processor'),
children: selectedType ? ( forceRender: true,
children: (
<Form form={form} layout="vertical" disabled={!selectedType} style={{ padding: '12px 0' }}> <Form form={form} layout="vertical" disabled={!selectedType} style={{ padding: '12px 0' }}>
{isDirectory && ( {selectedType ? (
<Text type="secondary" style={{ display: 'block', marginBottom: 12 }}> <>
{t('Directory processing always overwrites original files')} {isDirectory && (
</Text> <Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
{t('Directory processing always overwrites original files')}
</Text>
)}
<Form.Item
label={t('Target Path')}
required
>
<Flex gap={8} align="center">
<div style={{ flex: 1 }}>
<Form.Item
name="path"
rules={[{ required: true, message: t('Please select a path') }]}
noStyle
>
<Input placeholder={t('Select a path')} />
</Form.Item>
</div>
<Button onClick={() => openPathSelector('path', 'file')}>{t('Select File')}</Button>
<Button onClick={() => openPathSelector('path', 'directory')}>{t('Select Directory')}</Button>
</Flex>
</Form.Item>
<Form.Item
name="overwrite"
label={t('Overwrite original')}
valuePropName="checked"
>
<Switch disabled={isDirectory} />
</Form.Item>
{selectedProcessorMeta?.produces_file && !overwriteValue && (
<Form.Item label={t('Save To')}>
<Flex gap={8} align="center">
<div style={{ flex: 1 }}>
<Form.Item name="save_to" noStyle>
<Input placeholder={t('Optional output path')} />
</Form.Item>
</div>
<Button onClick={() => openPathSelector('save_to', 'any')}>{t('Select')}</Button>
</Flex>
</Form.Item>
)}
<ProcessorConfigForm
processorMeta={selectedProcessorMeta}
form={form}
configPath={['config']}
/>
<Form.Item>
<Button type="primary" onClick={handleRun} loading={running} disabled={!selectedType}>
{t('Run')}
</Button>
</Form.Item>
</>
) : (
<Empty style={{ marginTop: 64 }} description={t('Select a processor')} />
)} )}
<Form.Item
name="path"
label={t('Target Path')}
rules={[{ required: true, message: t('Please select a path') }]}
>
<Flex gap={8} align="center">
<Input placeholder={t('Select a path')} style={{ flex: 1 }} />
<Button onClick={() => openPathSelector('path', 'file')}>{t('Select File')}</Button>
<Button onClick={() => openPathSelector('path', 'directory')}>{t('Select Directory')}</Button>
</Flex>
</Form.Item>
<Form.Item
name="overwrite"
label={t('Overwrite original')}
valuePropName="checked"
>
<Switch disabled={isDirectory} />
</Form.Item>
{selectedProcessorMeta?.produces_file && !overwriteValue && (
<Form.Item
name="save_to"
label={t('Save To')}
>
<Flex gap={8} align="center">
<Input placeholder={t('Optional output path')} style={{ flex: 1 }} />
<Button onClick={() => openPathSelector('save_to', 'any')}>{t('Select')}</Button>
</Flex>
</Form.Item>
)}
<ProcessorConfigForm
processorMeta={selectedProcessorMeta}
form={form}
configPath={['config']}
/>
<Form.Item>
<Button type="primary" onClick={handleRun} loading={running} disabled={!selectedType}>
{t('Run')}
</Button>
</Form.Item>
</Form> </Form>
) : (
<Empty style={{ marginTop: 64 }} description={t('Select a processor')} />
), ),
}, },
]; ];
return ( return (
<PageCard title={t('Processors')}> <>
<Flex gap={16} style={{ height: '100%' }}> {contextHolder}
<Flex gap={16} style={{ height: 'calc(100vh - 88px)' }}>
<Card <Card
style={{ flex: '0 0 320px', minWidth: 280, display: 'flex', flexDirection: 'column' }} style={{ flex: '0 0 320px', minWidth: 280, display: 'flex', flexDirection: 'column' }}
title={t('Processor List')} title={t('Processor List')}
@@ -475,7 +494,7 @@ const ProcessorsPage = memo(function ProcessorsPage() {
onOk={handlePathSelected} onOk={handlePathSelected}
onCancel={() => setPathModalOpen(false)} onCancel={() => setPathModalOpen(false)}
/> />
</PageCard> </>
); );
}); });
+1 -1
View File
@@ -180,7 +180,7 @@ const TasksPage = memo(function TasksPage() {
width={480} width={480}
open={open} open={open}
onClose={() => { setOpen(false); setEditing(null); }} onClose={() => { setOpen(false); setEditing(null); }}
destroyOnClose destroyOnHidden
extra={ extra={
<Space> <Space>
<Button onClick={() => { setOpen(false); setEditing(null); }}>{t('Cancel')}</Button> <Button onClick={() => { setOpen(false); setEditing(null); }}>{t('Cancel')}</Button>