概述
本文深入分析Coze Studio中用户编辑数据库功能的前端实现。该功能允许用户在资源库中选择现有数据库进行编辑,修改其基本信息(名称、描述、图标等)和表结构,为开发者提供了灵活的数据库管理能力。通过对源码的详细解析,我们将了解从资源库入口到编辑页面的完整架构设计、组件实现、状态管理和用户体验优化等核心技术要点。
功能特性
核心功能
- 数据库基本信息编辑:支持修改数据库名称、描述和图标配置
- 数据库读写模式管理:提供数据库读写模式切换功能
- 表结构编辑:支持编辑数据库表结构
- 权限控制:基于用户权限动态显示编辑功能
- 实时更新:编辑完成后自动更新页面展示
- 表单验证:完善的编辑内容验证机制
用户体验特性
- 即时反馈:编辑操作结果实时展示和验证
- 表单验证:完善的数据库信息验证机制
- 便捷操作:通过表格行点击直接进入数据库详情页面
- 图标智能生成:自动生成数据库图标
- 国际化支持:多语言界面适配
技术架构
整体架构设计
┌─────────────────────────────────────────────────────────────┐
│ 数据库编辑模块 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ LibraryPage │ │BaseLibrary │ │DatabaseDetail │ │
│ │ (资源库页面) │ │ Page │ │ (数据库详情页面) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │Table组件 │ │Database │ │BaseInfoModal │ │
│ │ (表格展示) │ │ Header组件 │ │ (基本信息编辑模态框) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 状态管理层 │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │useDatabaseConfig│ │ handleEditBasicInfo 函数 │ │
│ │ (配置逻辑) │ │ (编辑逻辑处理) │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ API服务层 │
│ ┌─────────────────────────────────────────────────────────┐
│ │ Memory API │
│ │ UpdateDatabase API │
│ └─────────────────────────────────────────────────────────┘
└────────────────────────────────────────────────────────────┘
核心模块结构
frontend/
├── apps/coze-studio/src/
│ └── pages/
│ ├── library.tsx # 资源库入口页面
│ └── database/
│ ├── layout.tsx # 数据库页面布局
│ ├── page.tsx # 数据库详情页面
├── packages/studio/workspace/
│ ├── entry-adapter/src/pages/library/
│ │ └── index.tsx # LibraryPage适配器组件
│ └── entry-base/src/pages/library/
│ ├── index.tsx # BaseLibraryPage核心组件
│ └── hooks/use-entity-configs/
│ └── use-database-config.tsx # 数据库配置Hook
├── packages/data/memory/
│ ├── database-v2-main/src/
│ │ └── components/database-detail/
│ │ └── index.tsx # 数据库详情页面组件
│ ├── database-v2-base/src/
│ │ └── components/base-info-modal/
│ │ └── index.tsx # 基本信息编辑模态框
│ └── database-v2-adapter/src/
│ └── components/base-info-modal/
│ └── index.tsx # 基本信息编辑模态框适配器
├── packages/arch/idl/src/auto-generated/
│ └── memory/
│ └── database.ts # 数据库相关类型定义
└── packages/arch/bot-api/src/
└── memory-api.ts # MemoryApi定义(含UpdateDatabase)
用户编辑数据库流程概述
用户登录Coze Studio
↓
点击"资源库"菜单
↓
LibraryPage 组件加载
↓
在表格中点击要编辑的数据库行
↓
导航到数据库详情页面
↓
DatabaseDetail 组件加载并展示数据库信息
↓
用户点击数据库名称旁边的编辑按钮
↓
BaseInfoModal 模态框显示,预填充数据库信息
↓
用户修改数据库名称、描述或图标
↓
表单实时验证(名称必填且不能包含特殊字符)
↓
用户点击"确认"按钮
↓
handleSubmit 回调触发
↓
handleEditBasicInfo 函数调用
↓
MemoryApi.UpdateDatabase() 调用
↓
后端更新数据库信息
↓
fetchDatabaseInfo 重新获取数据库信息
↓
更新本地状态(setDatabaseInfo)
↓
关闭模态框,页面显示更新后的数据库信息
该流程包含多层验证和处理:
- 权限控制:通过isReadOnlyMode状态控制编辑按钮的显示
- 前端表单验证:通过Form组件进行名称必填验证和格式验证
- 数据预填充:编辑模态框自动填充当前数据库的信息
- API调用:使用MemoryApi.UpdateDatabase API处理数据库更新
- 状态同步:更新成功后同步更新页面状态
- 用户体验优化:提供图标自动生成功能
整个流程确保了数据库编辑的便捷性和用户体验的流畅性。
核心组件实现
组件层次结构
数据库编辑功能涉及多个层次的组件:
- LibraryPage组件:资源库主页面
- BaseLibraryPage组件:资源库核心逻辑
- DatabaseDetail组件:数据库详情页面,包含编辑功能
- BaseInfoModal组件:数据库基本信息编辑模态框
- useDatabaseConfig Hook:管理数据库在资源库中的配置状态
1. 资源库入口组件(LibraryPage)
文件位置:frontend/packages/studio/workspace/entry-adapter/src/pages/library/index.tsx
作为资源库的适配器组件,整合各种资源配置,包括数据库的展示和操作:
typescript
import { type FC, useRef } from 'react';
import {
BaseLibraryPage,
useDatabaseConfig,
usePluginConfig,
useWorkflowConfig,
usePromptConfig,
useKnowledgeConfig,
} from '@coze-studio/workspace-base/library';
export const LibraryPage: FC<{ spaceId: string }> = ({ spaceId }) => {
const basePageRef = useRef<{ reloadList: () => void }>(null);
const configCommonParams = {
spaceId,
reloadList: () => {
basePageRef.current?.reloadList();
},
};
const { config: pluginConfig, modals: pluginModals } =
usePluginConfig(configCommonParams);
const { config: workflowConfig, modals: workflowModals } =
useWorkflowConfig(configCommonParams);
const { config: knowledgeConfig, modals: knowledgeModals } =
useKnowledgeConfig(configCommonParams);
const { config: promptConfig, modals: promptModals } =
usePromptConfig(configCommonParams);
// 数据库配置,包含数据库相关操作
const { config: databaseConfig, modals: databaseModals } =
useDatabaseConfig(configCommonParams);
return (
<>
<BaseLibraryPage
spaceId={spaceId}
ref={basePageRef}
entityConfigs={[
pluginConfig,
workflowConfig,
knowledgeConfig,
promptConfig,
databaseConfig,
]}
/>
{pluginModals}
{workflowModals}
{knowledgeModals}
{promptModals}
{databaseModals} {/* 包含创建数据库的模态框 */}
</>
);
};
设计亮点:
- 状态集中管理:通过统一的reloadList机制管理各资源类型的状态更新
- 组件复用:BaseLibraryPage作为核心组件,通过配置系统支持多种资源类型
2. 资源库核心组件(BaseLibraryPage)
文件位置:frontend/packages/studio/workspace/entry-base/src/pages/library/index.tsx
负责资源库的核心展示逻辑,包括数据库列表的渲染和导航到详情页面的功能:
typescript
import { forwardRef, useImperativeHandle } from 'react';
import classNames from 'classnames';
import { useInfiniteScroll } from 'ahooks';
import { I18n } from '@coze-arch/i18n';
import {
Table,
Select,
Search,
Layout,
Cascader,
Space,
} from '@coze-arch/coze-design';
import { renderHtmlTitle } from '@coze-arch/bot-utils';
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
import {
type ResType,
type LibraryResourceListRequest,
type ResourceInfo,
} from '@coze-arch/idl/plugin_develop';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { type ListData, type BaseLibraryPageProps } from './types';
import { LibraryHeader } from './components/library-header';
export const BaseLibraryPage = forwardRef<
{ reloadList: () => void },
BaseLibraryPageProps
>(
({ spaceId, isPersonalSpace = true, entityConfigs }, ref) => {
const { params, setParams, resetParams, hasFilter, ready } =
useCachedQueryParams({
spaceId,
});
// 获取资源列表,包括数据库
const listResp = useInfiniteScroll<ListData>(
async prev => {
if (!ready) {
return {
list: [],
nextCursorId: undefined,
hasMore: false,
};
}
const resp = await PluginDevelopApi.LibraryResourceList(
entityConfigs.reduce<LibraryResourceListRequest>(
(res, config) => config.parseParams?.(res) ?? res,
{
...params,
cursor: prev?.nextCursorId,
space_id: spaceId,
size: LIBRARY_PAGE_SIZE,
},
),
);
return {
list: resp?.resource_list || [],
nextCursorId: resp?.cursor,
hasMore: !!resp?.has_more,
};
},
{
reloadDeps: [params, spaceId],
},
);
useImperativeHandle(ref, () => ({
reloadList: listResp.reload,
}));
return (
<Layout
className={s['layout-content']}
title={renderHtmlTitle(I18n.t('navigation_workspace_library'))}
>
<Layout.Header className={classNames(s['layout-header'], 'pb-0')}>
<div className="w-full">
<LibraryHeader entityConfigs={entityConfigs} />
{/* 过滤器组件 */}
</div>
</Layout.Header>
<Layout.Content>
{/* 表格和列表内容,点击数据库行可导航到详情页进行编辑 */}
</Layout.Content>
</Layout>
);
}
);
设计亮点:
- 无限滚动 :使用
useInfiniteScroll
实现资源列表的无限滚动加载 - 配置化设计:通过entityConfigs配置支持多种资源类型的展示
- 参数缓存 :使用
useCachedQueryParams
管理筛选和分页参数
3. 数据库配置Hook(useDatabaseConfig)
文件位置:frontend/packages/studio/workspace/entry-base/src/pages/library/hooks/use-entity-configs/use-database-config.tsx
管理数据库在资源库中的配置状态、列表渲染和操作权限:
typescript
import { useNavigate } from 'react-router-dom';
import { useRequest } from 'ahooks';
import {
ActionKey,
type ResourceInfo,
ResType,
} from '@coze-arch/idl/plugin_develop';
import { I18n } from '@coze-arch/i18n';
import { IconCozDatabase } from '@coze-arch/coze-design/icons';
import { Menu, Table, Toast } from '@coze-arch/coze-design';
import { MemoryApi } from '@coze-arch/bot-api';
import { useLibraryCreateDatabaseModal } from '@coze-data/database-v2';
import { type UseEntityConfigHook } from './types';
const { TableAction } = Table;
export const useDatabaseConfig: UseEntityConfigHook = ({
spaceId,
reloadList,
getCommonActions,
}) => {
const navigate = useNavigate();
const {
modal: createDatabaseModal,
open: openCreateDatabaseModal,
close: closeCreateDatabaseModal,
} = useLibraryCreateDatabaseModal({
enterFrom: 'library',
onFinish: databaseID => {
navigate(
`/space/${spaceId}/database/${databaseID}?page_modal=normal&biz=create`,
);
closeCreateDatabaseModal();
},
});
// delete action
const { run: deleteDatabase } = useRequest(
(databaseId: string) =>
MemoryApi.DeleteDatabase({
id: databaseId,
}),
{
manual: true,
onSuccess: () => {
reloadList();
Toast.success(I18n.t('Delete_success'));
},
},
);
return {
modals: <>{createDatabaseModal}</>,
config: {
typeFilter: {
label: I18n.t('new_db_001'),
value: ResType.Database,
},
renderCreateMenu: () => (
<Menu.Item
data-testid="workspace.library.header.create.card"
icon={<IconCozDatabase />}
onClick={openCreateDatabaseModal}
>
{I18n.t('new_db_001')}
</Menu.Item>
),
target: [ResType.Database],
// 点击数据库项导航到详情页,在详情页可以进行编辑操作
onItemClick: (item: ResourceInfo) => {
navigate(
`/space/${spaceId}/database/${item.res_id}?page_mode=normal&from=library`,
);
},
renderActions: (item: ResourceInfo) => {
// Can it be deleted?
const deleteDisabled = !item.actions?.find(
action => action.key === ActionKey.Delete,
)?.enable;
// delete operation
const deleteProps = {
disabled: deleteDisabled,
deleteDesc: I18n.t('library_delete_desc'),
handler: () => {
deleteDatabase(item.res_id || '');
},
};
return (
<TableAction
deleteProps={deleteProps}
actionList={getCommonActions?.(item)}
/>
);
},
},
};
};
设计亮点:
- 导航配置 :通过
onItemClick
定义数据库项的点击行为,导航到数据库详情页 - 操作权限 :根据资源的
actions
配置动态显示删除等操作 - 创建功能:集成了创建数据库的模态框功能
4. 数据库详情组件(DatabaseDetail)
文件位置:frontend/packages/data/memory/database-v2-main/src/components/database-detail/index.tsx
数据库详情页面的核心组件,包含基本信息展示和编辑功能:
typescript
import React, { useState, useEffect, useMemo } from 'react';
import { pick } from 'lodash-es';
import classNames from 'classnames';
import { userStoreService } from '@coze-studio/user-store';
import { type DatabaseInfo as DatabaseInitInfo } from '@coze-studio/bot-detail-store';
import { type WidgetUIState } from '@coze-data/knowledge-stores';
import { BotE2e } from '@coze-data/e2e';
import { DatabaseTabs } from '@coze-data/database-v2-base/types';
import { DismissibleBanner } from '@coze-data/database-v2-base/components/dismissible-banner';
import {
type FormData,
ModalMode,
} from '@coze-data/database-v2-base/components/base-info-modal';
import { DatabaseModeSelect } from '@coze-data/database-v2-adapter/components/database-mode-select';
import { DatabaseCreateTableModal } from '@coze-data/database-v2-adapter/components/create-table-modal';
import { DatabaseBaseInfoModal } from '@coze-data/database-v2-adapter/components/base-info-modal';
import { DatabaseDetailWaring } from '@coze-data/database-v2-adapter';
import { I18n } from '@coze-arch/i18n';
import {
IconCozEdit,
IconCozCross,
IconCozArrowLeft,
} from '@coze-arch/coze-design/icons';
import {
Button,
IconButton,
TabBar,
Toast,
CozAvatar,
Typography,
Space,
} from '@coze-arch/coze-design';
import {
BotTableRWMode,
TableType,
type DatabaseInfo,
type UpdateDatabaseRequest,
} from '@coze-arch/bot-api/memory';
import { MemoryApi } from '@coze-arch/bot-api';
export const DatabaseDetail = ({
version,
enterFrom,
initialTab,
needHideCloseIcon = false,
addRemoveButtonText,
onClose,
onClickAddRemoveButton,
onIDECallback,
onAfterEditBasicInfo,
onAfterEditRecords,
databaseId,
}: DatabaseDetailProps) => {
const userId = userStoreService.useUserInfo()?.user_id_str;
const [basicInfoVisible, setBasicInfoVisible] = useState(false);
const [createTableVisible, setCreateTableVisible] = useState(false);
// database basicInfo
const [databaseInfo, setDatabaseInfo] = useState<DatabaseInfo>({});
// tab key
const [activeKey, setActiveKey] = useState(
version ? DatabaseTabs.Structure : initialTab ?? DatabaseTabs.Structure,
);
// btn loading
const [btnLoading, setBtnLoading] = useState(false);
// page loading
const [loading, setLoading] = useState(true);
// fetch database basicInfo
const fetchDatabaseInfo = async () => {
try {
setLoading(true);
const response = await MemoryApi.GetDatabaseByID({
id: databaseId,
...(version ? { version } : {}),
});
if (response.database_info) {
setDatabaseInfo(response.database_info);
if (response.database_info.table_name) {
onIDECallback?.onUpdateDisplayName?.(response.database_info.table_name);
onIDECallback?.onStatusChange?.('normal');
}
} else {
onIDECallback?.onStatusChange?.('error');
}
} catch {
onIDECallback?.onStatusChange?.('error');
} finally {
setLoading(false);
}
};
// 权限控制:判断是否为只读模式
const isReadOnlyMode = databaseInfo.creator_id !== userId || !!version;
// 数据库基本信息编辑处理函数
const handleEditBasicInfo = async (obj: UpdateDatabaseRequest) => {
try {
const response = await MemoryApi.UpdateDatabase({
...pick(databaseInfo, [
'id',
'icon_uri',
'table_name',
'table_desc',
'field_list',
'rw_mode',
'prompt_disabled',
'extra_info',
]),
...obj,
});
if (response?.database_info?.id) {
await fetchDatabaseInfo();
// update basicInfo callback
if (onAfterEditBasicInfo) {
onAfterEditBasicInfo();
}
// close basicInfo modal
if (basicInfoVisible) {
setBasicInfoVisible(false);
}
} else {
Toast.error('Update database failed');
}
} catch (error) {
Toast.error('Failed to update database: ' + (error instanceof Error ? error.message : String(error)));
}
};
// 初始化数据
const basicInitData: FormData = useMemo(
() => ({
name: databaseInfo.table_name || '',
description: databaseInfo.table_desc || '',
icon_uri: [
{
url: databaseInfo.icon_url || '',
uri: databaseInfo.icon_uri || '',
uid: databaseInfo.icon_uri || '',
isDefault: true,
},
],
}),
[databaseInfo],
);
useEffect(() => {
fetchDatabaseInfo();
}, []);
return (
<>
<div
className={classNames(
'h-full w-full max-w-[100vw] flex flex-col overflow-hidden',
enterFrom === 'project'
? 'coz-bg-max rounded-b-[8px] border-solid coz-stroke-primary'
: 'coz-bg-plus',
)}
>
{/* header - 包含数据库名称、描述和编辑按钮 */}
<div
className={classNames(
'flex flex-row items-center justify-between shrink-0',
enterFrom === 'library'
? 'h-[40px] m-[24px]'
: 'h-[64px] px-[16px] py-[12px] border-0 border-b border-solid coz-stroke-primary',
)}
>
<div className="flex items-center gap-[8px]">
{/* 图标、名称和编辑按钮 */}
<CozAvatar
type="bot"
color="grey"
src={basicInitData.icon_uri?.[0]?.url}
/>
<div className="flex flex-col">
<div className="flex flex-row items-center gap-[2px] leading-none">
<Typography.Text weight={500} fontSize="14px">
{basicInitData.name}
</Typography.Text>
{/* 根据权限控制显示编辑按钮 */}
{isReadOnlyMode ? null : (
<IconButton
size="mini"
color="secondary"
icon={<IconCozEdit className="coz-fg-secondary" />}
onClick={() => setBasicInfoVisible(true)}
/>
)}
</div>
<Typography.Text fontSize="12px">
{basicInitData.description}
</Typography.Text>
</div>
</div>
{/* 读写模式切换 */}
</div>
{/* 基本信息编辑模态框 */}
<DatabaseBaseInfoModal
visible={basicInfoVisible}
mode={ModalMode.EDIT}
initValues={basicInitData}
onClose={() => setBasicInfoVisible(false)}
onSubmit={handleEditBasicInfo}
/>
</div>
</>
);
};
设计亮点:
- 权限控制 :通过
isReadOnlyMode
状态判断用户是否有权限编辑数据库 - 数据同步 :编辑完成后通过
fetchDatabaseInfo
重新获取数据,确保状态一致性 - 状态管理:使用多个state管理模态框显示、加载状态等
- 组件化:将编辑功能封装在独立的模态框组件中
5. 数据库基本信息编辑模态框(DatabaseBaseInfoModal)
文件位置:frontend/packages/data/memory/database-v2-base/src/components/base-info-modal/index.tsx
实现数据库基本信息编辑的核心模态框组件:
typescript
import { useState, type FC, useRef, useEffect, useCallback } from 'react';
import { CozeFormTextArea, CozeInputWithCountField } from '@coze-data/utils';
import {
PictureUpload,
type RenderAutoGenerateParams,
} from '@coze-common/biz-components/picture-upload';
import { I18n } from '@coze-arch/i18n';
import { Form, type FormApi, Modal } from '@coze-arch/coze-design';
import { FormatType } from '@coze-arch/bot-api/memory';
import { FileBizType, IconType } from '@coze-arch/bot-api/developer_api';
import { KnowledgeApi } from '@coze-arch/bot-api';
interface DatabaseBaseInfoModalProps {
visible: boolean;
mode: ModalMode;
initValues?: FormData;
onClose: () => void;
onSubmit: (data: any) => void;
renderAutoGenerate?: (params: RenderAutoGenerateParams) => React.ReactNode;
}
export enum ModalMode {
CREATE = 'create',
EDIT = 'edit',
}
export interface FormData {
name: string;
description: string;
icon_uri?: Array<{
url: string;
uri: string;
uid?: string;
isDefault?: boolean;
}>;
}
export const DatabaseBaseInfoModal: FC<DatabaseBaseInfoModalProps> = ({
visible,
initValues,
onClose,
onSubmit,
mode,
renderAutoGenerate,
}) => {
const formRef = useRef<FormApi<FormData> | null>(null);
const [coverIcon, setCoverIcon] = useState<{ uri: string; url: string }>({ uri: '', url: '' });
const [iconInfoGenerate, setIconInfoGenerate] = useState<{ name: string; desc: string }>({ name: '', desc: '' });
// 提交表单处理
const handleSubmit = async () => {
if (!formRef.current) {
return;
}
try {
const formData = await formRef.current.validate();
onSubmit({
...formData,
icon_uri: [
{
url: formData?.icon_uri?.[0]?.url ?? '',
uri: formData?.icon_uri?.[0]?.uid ?? '',
},
],
});
} catch (validationError) {
// Form validation failed, errors will be displayed by the Form component
console.error('Form validation failed:', validationError);
}
};
// 处理关闭
const handleClose = () => {
onClose();
};
// 设置默认图标
const setDefaultIcon = async () => {
try {
const { icon } = await KnowledgeApi.GetIcon({
format_type: FormatType.Database,
});
setCoverIcon({
uri: icon?.uri ?? '',
url: icon?.url ?? '',
});
formRef.current?.setValue('icon_uri', [
{
url: icon?.url ?? '',
uri: icon?.uri ?? '',
uid: icon?.uri ?? '',
isDefault: true,
},
]);
} catch (error) {
console.error('Failed to set default icon:', error);
}
};
// 初始化表单数据
const initForm = useCallback(
({ name, description, icon_uri }: FormData) => {
if (!formRef.current) {
return;
}
formRef.current.setValue('name', name);
formRef.current.setValue('description', description);
setIconInfoGenerate({
name: name ?? '',
desc: description ?? '',
});
if (!icon_uri || !icon_uri[0]?.url) {
setDefaultIcon();
return;
}
formRef.current.setValue('icon_uri', [
{
url: icon_uri[0].url,
uri: icon_uri[0].uri,
uid: icon_uri[0].uri,
isDefault: true,
},
]);
},
[formRef],
);
// 当模态框显示时初始化表单
useEffect(() => {
if (!visible) {
return;
}
if (!initValues) {
return;
}
initForm(initValues);
}, [visible, initValues, initForm]);
return (
<Modal
title={I18n.t(mode === ModalMode.CREATE ? 'new_db_001' : 'new_db_003')}
open={visible}
onCancel={handleClose}
onOk={handleSubmit}
>
{/* 表单内容:包含名称、描述和图标上传 */}
<Form ref={formRef} layout="vertical">
{/* 名称输入字段 */}
<Form.Item
label={I18n.t('new_db_004')}
name="name"
rules={[
{ required: true, message: I18n.t('new_db_009') },
{ max: 100, message: I18n.t('new_db_010') },
{ pattern: /^[^"]*$/, message: I18n.t('database_name_cannot_contain_special_characters') },
]}
>
<CozeInputWithCountField
placeholder={I18n.t('new_db_011')}
maxLength={100}
/>
</Form.Item>
{/* 描述输入字段 */}
<Form.Item
label={I18n.t('new_db_005')}
name="description"
rules={[
{ max: 500, message: I18n.t('new_db_012') },
]}
>
<CozeFormTextArea
placeholder={I18n.t('new_db_013')}
maxLength={500}
rows={4}
/>
</Form.Item>
{/* 图标上传 */}
<Form.Item
label={I18n.t('new_db_006')}
name="icon_uri"
>
<PictureUpload
type={IconType.Database}
renderAutoGenerate={renderAutoGenerate}
bizType={FileBizType.DATABASE_ICON}
/>
</Form.Item>
</Form>
</Modal>
);
};
// 数据库编辑模态框的完整实现已在前面章节展示
设计亮点:
- 权限控制:通过参数配置确保只有有权限的用户才能编辑知识库
- 表单验证:完善的编辑表单验证逻辑,包括必填项验证和格式验证
- 图标上传:支持自定义图标上传功能,提升知识库识别度
- 状态管理:清晰的加载状态和错误处理机制
- API交互:封装了知识库详情获取和更新的API调用逻辑
- 错误处理:统一的错误捕获和用户反馈机制
- 用户体验优化:表单预填充和操作成功提示