From 55b4e0ebd379d3b90e1885310d248997f820c66c Mon Sep 17 00:00:00 2001 From: yujiangping Date: Tue, 10 Feb 2026 12:03:38 +0800 Subject: [PATCH] feat(web): refactor knowledge base form state management and field synchronization - Add Form.useWatch hook to monitor _third_party_platform field changes directly - Implement useEffect to sync form value to thirdPartyPlatform state when platform changes - Remove redundant conditional field assignments for third-party and web parser configs - Consolidate third-party platform state initialization in setBaseFields function - Update Feishu parameter naming from generic (app_id, app_secret, folder_token) to prefixed format (feishu_app_id, feishu_app_secret, feishu_folder_token) - Rename third_party_platform field to _third_party_platform for consistency - Optimize useEffect dependencies to prevent unnecessary re-renders and state inconsistencies - Improve form field initialization logic to handle both create and edit modes correctly - Simplify third-party platform state management by centralizing it in setBaseFields instead of multiple locations --- .../KnowledgeBase/components/CreateModal.tsx | 125 ++++++++---------- .../components/RecallTestResult.tsx | 14 ++ web/src/views/KnowledgeBase/types.ts | 8 +- 3 files changed, 75 insertions(+), 72 deletions(-) diff --git a/web/src/views/KnowledgeBase/components/CreateModal.tsx b/web/src/views/KnowledgeBase/components/CreateModal.tsx index cfefc240..1094a511 100644 --- a/web/src/views/KnowledgeBase/components/CreateModal.tsx +++ b/web/src/views/KnowledgeBase/components/CreateModal.tsx @@ -46,6 +46,16 @@ const CreateModal = forwardRef(({ const entityTypes = graphragConfig?.entity_types || ''; const entityNormalization = graphragConfig?.resolution || false; const communityReportGeneration = graphragConfig?.community || false; + + // Watch for changes to _third_party_platform field directly + const formThirdPartyPlatform = Form.useWatch(['parser_config', '_third_party_platform'], form); + + // Sync form value to state when form value changes + useEffect(() => { + if (formThirdPartyPlatform && (formThirdPartyPlatform === 'yuque' || formThirdPartyPlatform === 'feishu')) { + setThirdPartyPlatform(formThirdPartyPlatform); + } + }, [formThirdPartyPlatform]); // Encapsulate cancel method, add close modal logic const handleClose = () => { @@ -199,6 +209,8 @@ const CreateModal = forwardRef(({ type: type || currentType, }; form.setFieldsValue(defaults); + // Reset third party platform to default when creating new + setThirdPartyPlatform('yuque'); return; } const baseValues: Partial = { @@ -210,7 +222,6 @@ const CreateModal = forwardRef(({ }; // Process parser_config data, set default values if not present - const recordAny = record as any; baseValues.parser_config = { ...record.parser_config, graphrag: { @@ -224,43 +235,6 @@ const CreateModal = forwardRef(({ } }; - // Add Third-party specific fields to parser_config if exists - if (recordAny.parser_config?.third_party_platform) { - baseValues.parser_config.third_party_platform = recordAny.parser_config.third_party_platform; - } - if (recordAny.parser_config?.yuque_user_id) { - baseValues.parser_config.yuque_user_id = recordAny.parser_config.yuque_user_id; - } - if (recordAny.parser_config?.yuque_token) { - baseValues.parser_config.yuque_token = recordAny.parser_config.yuque_token; - } - if (recordAny.parser_config?.app_id) { - baseValues.parser_config.app_id = recordAny.parser_config.app_id; - } - if (recordAny.parser_config?.app_secret) { - baseValues.parser_config.app_secret = recordAny.parser_config.app_secret; - } - if (recordAny.parser_config?.folder_token) { - baseValues.parser_config.folder_token = recordAny.parser_config.folder_token; - } - - // Add Web specific fields to parser_config if exists - if (recordAny.parser_config?.entry_url) { - baseValues.parser_config.entry_url = recordAny.parser_config.entry_url; - } - if (recordAny.parser_config?.max_pages) { - baseValues.parser_config.max_pages = recordAny.parser_config.max_pages; - } - if (recordAny.parser_config?.delay_seconds) { - baseValues.parser_config.delay_seconds = recordAny.parser_config.delay_seconds; - } - if (recordAny.parser_config?.timeout_seconds) { - baseValues.parser_config.timeout_seconds = recordAny.parser_config.timeout_seconds; - } - if (recordAny.parser_config?.user_agent) { - baseValues.parser_config.user_agent = recordAny.parser_config.user_agent; - } - // If entity_types exists, convert to newline-separated format for TextArea display if (baseValues.parser_config.graphrag.entity_types) { if (Array.isArray(baseValues.parser_config.graphrag.entity_types)) { @@ -272,7 +246,18 @@ const CreateModal = forwardRef(({ } } + // Set form values first form.setFieldsValue(baseValues); + + // Then sync third party platform state from form value + // This ensures the state matches what's actually in the form + const platform = baseValues.parser_config?._third_party_platform; + if (platform === 'yuque' || platform === 'feishu') { + setThirdPartyPlatform(platform); + } else { + // Reset to default if no platform specified + setThirdPartyPlatform('yuque'); + } }; const setDynamicModelFields = (record: KnowledgeBaseListItem | null, types: string[]) => { @@ -295,20 +280,17 @@ const CreateModal = forwardRef(({ setDatasets(record || null); // If rebuild mode, use record's actual type, otherwise use passed type - const actualType = type === 'rebuild' ? (record?.type || 'General') : (type || currentType); + // If editing (record exists but no type passed), use record's type + const actualType = type === 'rebuild' + ? (record?.type || 'General') + : (type || record?.type || currentType); + setCurrentType(actualType as any); setIsRebuildMode(type === 'rebuild'); // Set rebuild mode flag setOriginalType(type || ''); // Save original type parameter - // Set third party platform if editing Third-party type - if (actualType === 'Third-party' && record) { - const platform = (record as any).parser_config?.third_party_platform; - if (platform === 'yuque' || platform === 'feishu') { - setThirdPartyPlatform(platform); - } - } else { - setThirdPartyPlatform('yuque'); // Reset to default - } + // Note: third party platform state will be set in setBaseFields function + // No need to set it here separately to avoid inconsistency // If rebuild mode, default to knowledge graph tab if (type === 'rebuild') { @@ -336,9 +318,13 @@ const CreateModal = forwardRef(({ useEffect(() => { if (!visible) return; - setBaseFields(datasets, currentType); - setDynamicModelFields(datasets, modelTypeList); - }, [visible, datasets, currentType, modelTypeList]); + // Only set fields when modal becomes visible, not on every state change + // setBaseFields is already called in handleOpen + // This useEffect is mainly for syncing dynamic model fields + if (datasets && modelTypeList.length > 0) { + setDynamicModelFields(datasets, modelTypeList); + } + }, [visible, modelTypeList]); // Encapsulate save method, add submit logic const handleSave = () => { @@ -382,7 +368,7 @@ const CreateModal = forwardRef(({ // Check Third-party authentication before saving if (formValues.type === 'Third-party' || currentType === 'Third-party') { - const platform = formValues.parser_config?.third_party_platform || thirdPartyPlatform; + const platform = formValues.parser_config?._third_party_platform || thirdPartyPlatform; try { if (platform === 'yuque') { @@ -404,9 +390,9 @@ const CreateModal = forwardRef(({ } else if (platform === 'feishu') { // Validate Feishu credentials const feishuParams = { - feishu_app_id: formValues.parser_config?.app_id, - feishu_app_secret: formValues.parser_config?.app_secret, - feishu_folder_token: formValues.parser_config?.folder_token + feishu_app_id: formValues.parser_config?.feishu_app_id, + feishu_app_secret: formValues.parser_config?.feishu_app_secret, + feishu_folder_token: formValues.parser_config?.feishu_folder_token }; if (!feishuParams.feishu_app_id || !feishuParams.feishu_app_secret || !feishuParams.feishu_folder_token) { @@ -533,7 +519,7 @@ const CreateModal = forwardRef(({ { type: 'url', message: t('knowledgeBase.createForm.entryUrlInvalid') } ]} > - + (({ @@ -553,12 +540,13 @@ const CreateModal = forwardRef(({ name={['parser_config', 'delay_seconds']} label={t('knowledgeBase.createForm.delaySeconds')} rules={[{ required: true, message: t('knowledgeBase.createForm.delaySecondsRequired') }]} - initialValue={1.0} + initialValue={2} > @@ -572,6 +560,7 @@ const CreateModal = forwardRef(({ min={5} max={15} step={1} + disabled={!!datasets?.id} /> @@ -581,7 +570,7 @@ const CreateModal = forwardRef(({ rules={[{ required: true, message: t('knowledgeBase.createForm.userAgentRequired') }]} initialValue="KnowledgeBaseCrawler/1.0" > - + )} @@ -596,8 +585,8 @@ const CreateModal = forwardRef(({ initialValue="yuque" > + (({ label={t('knowledgeBase.createForm.yuqueToken')} rules={[{ required: true, message: t('knowledgeBase.createForm.yuqueTokenRequired') }]} > - + )} @@ -632,23 +621,23 @@ const CreateModal = forwardRef(({ label={t('knowledgeBase.createForm.feishuAppId')} rules={[{ required: true, message: t('knowledgeBase.createForm.feishuAppIdRequired') }]} > - + - + - + )} diff --git a/web/src/views/KnowledgeBase/components/RecallTestResult.tsx b/web/src/views/KnowledgeBase/components/RecallTestResult.tsx index f9a9bb6e..98c67fd4 100644 --- a/web/src/views/KnowledgeBase/components/RecallTestResult.tsx +++ b/web/src/views/KnowledgeBase/components/RecallTestResult.tsx @@ -100,6 +100,20 @@ const RecallTestResult = ({ } }; + // Show skeleton when initial loading + if (loading && data.length === 0) { + return ( +
+
+ {t('knowledgeBase.recallResult')} +
+ + + +
+ ); + } + if (data.length === 0 && showEmpty) { return (