刪除插件邏輯
1. 刪除操作入口組件
刪除插件操作主要通過 usePluginConfig
hook 中的 renderActions
方法實現,該方法返回 TableAction
組件來處理表格行的操作。
文件位置:frontend/packages/studio/workspace/entry-base/src/pages/library/hooks/use-entity-configs/use-plugin-config.tsx
核心實現邏輯:
// 在 usePluginConfig hook 中
renderActions: (item: ResourceInfo) => {const deleteDisabled = !item.actions?.find(action => action.key === ActionKey.Delete,)?.enable;const deleteProps = {disabled: deleteDisabled,deleteDesc: I18n.t('library_delete_desc'),handler: async () => {await PluginDevelopApi.DelPlugin({ plugin_id: item.res_id });reloadList();Toast.success(I18n.t('Delete_success'));},};return (<TableActiondeleteProps={deleteProps}actionList={getCommonActions?.(item)}/>);
}
設計亮點:
- 權限控制:基于后端返回的
actions
數組動態控制按鈕狀態 - 國際化支持:使用
I18n.t()
進行多語言支持 - 操作集成:同時支持刪除、編輯等多種操作
2. 刪除插件的核心Hook實現
文件位置:frontend/packages/studio/workspace/entry-base/src/pages/library/hooks/use-entity-configs/use-plugin-config.tsx
核心代碼:
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
import {ActionKey,PluginType,ResType,type ResourceInfo,
} from '@coze-arch/idl/plugin_develop';
import { I18n } from '@coze-arch/i18n';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { useBotCodeEditOutPlugin } from '@coze-agent-ide/bot-plugin/hook';
import { CreateFormPluginModal } from '@coze-agent-ide/bot-plugin/component';
import { IconCozPlugin } from '@coze-arch/coze-design/icons';
import { Menu, Tag, Toast, Table } from '@coze-arch/coze-design';const { TableAction } = Table;export const usePluginConfig: UseEntityConfigHook = ({spaceId,reloadList,getCommonActions,
}) => {const [showFormPluginModel, setShowFormPluginModel] = useState(false);const navigate = useNavigate();const { modal: editPluginCodeModal, open } = useBotCodeEditOutPlugin({modalProps: {onSuccess: reloadList,},});return {config: {renderActions: (item: ResourceInfo) => {const deleteDisabled = !item.actions?.find(action => action.key === ActionKey.Delete,)?.enable;const deleteProps = {disabled: deleteDisabled,deleteDesc: I18n.t('library_delete_desc'),handler: async () => {await PluginDevelopApi.DelPlugin({ plugin_id: item.res_id });reloadList();Toast.success(I18n.t('Delete_success'));},};return (<TableActiondeleteProps={deleteProps}actionList={getCommonActions?.(item)}/>);},},};
};
設計亮點:
- 異步狀態管理:使用
useRequest
管理刪除請求狀態 - 錯誤處理:完善的成功/失敗回調處理
- 用戶反饋:及時的Toast提示信息
- 列表刷新:刪除成功后自動刷新資源列表
3. 刪除確認彈窗邏輯
文件位置:frontend/packages/components/bot-semi/src/components/ui-table-action/index.tsx
核心代碼:
// 刪除確認彈窗的實現
<Popconfirmtrigger="click"okType="danger"title={i18n.t('delete_title')}content={i18n.t('plugin_delete_confirm_desc')}okText={i18n.t('confirm')}cancelText={i18n.t('cancel')}style={{ width: 350 }}icon={deleteProps?.popconfirm?.icon ?? <IconWaringRed />}{...deleteProps.popconfirm}onConfirm={deleteProps?.handler}disabled={deleteProps.disabled}
><span><Tooltipspacing={12}content={i18n.t('Delete')}position="top"{...deleteProps.tooltip}><UIIconButtondisabled={deleteProps.disabled}icon={<IconDeleteOutline className={styles.icon} />}style={iconColor('delete')}onClick={deleteProps.handleClick}data-testid="ui.table-action.delete"/></Tooltip></span>
</Popconfirm>
設計亮點:
- 權限控制:基于后端返回的actions數組動態控制刪除按鈕狀態
- 確認機制:使用Popconfirm組件提供刪除確認彈窗
- 錯誤處理:完善的錯誤提示和異常處理
- 用戶反饋:及時的成功/失敗提示
4. TableAction 組件實現
文件位置:@coze-arch/coze-design
包中的 Table.TableAction
組件
核心代碼:
import { Table } from '@coze-arch/coze-design';const { TableAction } = Table;// TableAction 組件的使用方式
<TableActiondeleteProps={{disabled: deleteDisabled,deleteDesc: I18n.t('library_delete_desc'),handler: async () => {await PluginDevelopApi.DelPlugin({ plugin_id: item.res_id });reloadList();Toast.success(I18n.t('Delete_success'));},}}actionList={getCommonActions?.(item)}
/>
設計亮點:
- 組件復用:來自 @coze-arch/coze-design 的統一表格操作組件
- 配置化:通過 deleteProps 和 actionList 配置操作
- 權限控制:基于后端返回的 actions 數組控制操作權限
- 確認機制:內置刪除確認彈窗機制
5. 插件配置Hook(usePluginConfig)完整實現
文件位置:frontend/packages/studio/workspace/entry-base/src/pages/library/hooks/use-entity-configs/use-plugin-config.tsx
管理插件的刪除功能和狀態的完整實現:
import { useNavigate } from 'react-router-dom';
import { useRef } from 'react';
import { useRequest } from 'ahooks';
import {ActionKey,ResType,type ResourceInfo,
} from '@coze-arch/idl/plugin_develop';
import { I18n } from '@coze-arch/i18n';
import { IconCozPlugin } from '@coze-arch/coze-design/icons';
import { Table, Menu, Toast } from '@coze-arch/coze-design';
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
import { useFlags } from '@coze-arch/bot-flags';
import { PluginDevelopApi } from '@coze-arch/bot-api';
import { usePluginEditorModal } from '@coze-common/plugin-editor-modal';import { type UseEntityConfigHook } from './types';const { TableAction } = Table;export const usePluginConfig: UseEntityConfigHook = ({spaceId,isPersonalSpace = true,reloadList,getCommonActions,
}) => {const navigate = useNavigate();const [FLAGS] = useFlags();const recordRef = useRef<ResourceInfo | null>(null);const { open: openPluginEditor, node: pluginEditorModal } =usePluginEditorModal({spaceId,source: 'resource_library',onUpdateSuccess: reloadList,onPublish: ({ pluginId }) => {recordRef.current = {res_id: pluginId,};// 插件發布后的處理邏輯},});// 刪除插件的核心邏輯const { run: delPlugin } = useRequest((pluginId: string) =>PluginDevelopApi.DelPlugin({plugin_id: pluginId,}),{manual: true,onSuccess: () => {reloadList(); // 刪除成功后刷新列表Toast.success(I18n.t('Delete_success')); // 顯示成功提示},onError: (error) => {Toast.error(I18n.t('Delete_failed')); // 顯示失敗提示console.error('刪除插件失敗:', error);},},);return {modals: (<>{pluginEditorModal}</>),config: {typeFilter: {label: I18n.t('library_resource_type_plugin'),value: ResType.Plugin,},renderCreateMenu: () => (<Menu.Itemdata-testid="workspace.library.header.create.plugin"icon={<IconCozPlugin />}onClick={() => {sendTeaEvent(EVENT_NAMES.widget_create_click, {source: 'menu_bar',workspace_type: isPersonalSpace? 'personal_workspace': 'team_workspace',});openPluginEditor({mode: 'create',});}}>{I18n.t('create_new_plugin')}</Menu.Item>),target: [ResType.Plugin],onItemClick: (record: ResourceInfo) => {recordRef.current = record;const canEdit = record.actions?.find(action => action.key === ActionKey.Edit,)?.enable;openPluginEditor({mode: 'info',canEdit,editId: record.res_id || '',});},// 渲染表格操作列,包含刪除功能renderActions: (libraryResource: ResourceInfo) => (<TableActiondeleteProps={{// 根據權限控制刪除按鈕狀態disabled: !libraryResource.actions?.find(action => action.key === ActionKey.Delete,)?.enable,// 刪除確認描述deleteDesc: I18n.t('plugin_resource_delete_describ'),// 刪除處理函數handler: () => {delPlugin(libraryResource.res_id || '');},}}// 編輯操作editProps={{disabled: !libraryResource.actions?.find(action => action.key === ActionKey.Edit,)?.enable,handler: () => {openPluginEditor({mode: 'edit',editId: libraryResource.res_id || '',});},}}actionList={getCommonActions?.(libraryResource)}/>),},};
};
bot-api/package.json
文件位置:frontend/packages/arch/bot-api/package.json
核心代碼:
{"name": "@coze-arch/bot-api","version": "0.0.1","description": "RPC wrapper for bot studio application","author": "fanwenjie.fe@bytedance.com","exports": {".": "./src/index.ts"}
}
代碼作用:
- 包定義:定義了一個名為
@coze-arch/bot-api
的 npm 包,版本為 0.0.1,這是一個用于 bot studio 應用的 RPC 包裝器。 - API導出:在
frontend/packages/arch/bot-api/src/index.ts
中,PluginDevelopApi 被導出:
export { PluginDevelopApi } from './plugin-develop-api';
這允許通過 @coze-arch/bot-api
直接導入 PluginDevelopApi
。
3. API實現:在 src/plugin-develop-api.ts
中,PluginDevelopApi
是一個配置好的服務實例,它使用了 PluginDevelopService 和 axios 請求配置。
src/plugin-develop-api.ts
文件位置:frontend/packages/arch/bot-api/src/plugin-develop-api.ts
核心代碼:
import PluginDevelopApiService from './idl/plugin_develop';
import { axiosInstance, type BotAPIRequestConfig } from './axios';// eslint-disable-next-line @typescript-eslint/naming-convention
export const PluginDevelopApi = new PluginDevelopApiService<BotAPIRequestConfig>({request: (params, config = {}) => {config.headers = Object.assign(config.headers || {}, {'Agw-Js-Conv': 'str',});return axiosInstance.request({ ...params, ...config });},
});
axiosInstance說明
- 全局共享:axiosInstance 在整個項目中是全局共享的
- bot-api 包中的導入:
frontend/packages/arch/bot-api/src/axios.ts
直接從@coze-arch/bot-http
包導入了 axiosInstance
import {axiosInstance,isApiError,type AxiosRequestConfig,
} from '@coze-arch/bot-http';
- bot-http 包中的定義:
frontend/packages/arch/bot-http/src/axios.ts
export const axiosInstance = axios.create();
這里創建了一個全局的 axios 實例,與其他API請求使用的是同一個實例。
PluginDevelopApiService說明
- bot-api包中的導入路徑:
import PluginDevelopApiService from './idl/plugin_develop';
實際指向 frontend/packages/arch/bot-api/src/idl/plugin_develop.ts
文件內容重新導出了 @coze-arch/idl/plugin_develop
包的所有內容,包括默認導出:
export * from '@coze-arch/idl/plugin_develop';
export { default as default } from '@coze-arch/idl/plugin_develop';
- idl包的模塊映射:
文件位置:frontend/packages/arch/idl/package.json
{"name": "@coze-arch/idl","version": "0.0.1","description": "IDL files for bot studio application","author": "fanwenjie.fe@bytedance.com","exports": {"./plugin_develop": "./src/auto-generated/plugin_develop/index.ts"}
}
代碼作用:將 @coze-arch/idl/plugin_develop
映射到實際文件路徑 frontend/packages/arch/idl/src/auto-generated/plugin_develop/index.ts
6. 刪除插件操作的完整流程
刪除插件的完整流程如下:
流程說明:
- 權限檢查:基于后端返回的
actions
數組判斷是否有刪除權限 - 確認機制:通過
Popconfirm
組件進行二次確認 - API調用:使用
PluginDevelopApi.DelPlugin
執行刪除操作 - 狀態更新:刪除成功后自動刷新資源列表
- 用戶反饋:通過
Toast
組件顯示操作結果