feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,215 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { type ComponentProps, Suspense, forwardRef, lazy } from 'react';
import classNames from 'classnames';
import { I18n } from '@coze-arch/i18n';
import {
type BotSpace,
SpaceType,
type DraftBot,
} from '@coze-arch/bot-api/developer_api';
import { type UploadValue } from '@coze-common/biz-components/picture-upload';
import { IconTeamDefault } from '@coze-arch/bot-icons';
import { botInputLengthService } from '@coze-agent-ide/bot-input-length-limit';
import {
FormTextArea,
FormInput,
Tag,
Form,
FormSelect,
Avatar,
Typography,
} from '@coze-arch/coze-design';
import { FormSwitch } from './form-switch';
import s from './index.module.less';
const { Text } = Typography;
const LazyReactMarkdown = lazy(() => import('react-markdown'));
const ReactMarkdown = (props: ComponentProps<typeof LazyReactMarkdown>) => (
<Suspense fallback={null}>
<LazyReactMarkdown {...props} />
</Suspense>
);
export type AgentInfoFormValue = Partial<{
bot_uri: UploadValue;
name: string;
target: string;
spaceId?: string;
enableMonetize?: boolean;
}>;
export interface AgentInfoFormProps {
className?: string;
mode: 'add' | 'update';
showSpace: boolean;
initialValues: Partial<DraftBot>;
spacesList: BotSpace[];
currentSpaceId?: string; // Current space ID from store
hideOperation?: boolean; // hide_operation from store
checkErr: boolean;
errMsg: string;
onValuesChange: (values: AgentInfoFormValue) => void;
slot?: React.ReactNode;
}
export const AgentInfoForm = forwardRef<
Form<AgentInfoFormValue>,
AgentInfoFormProps
>(
// eslint-disable-next-line complexity
(
{
className,
mode,
showSpace,
initialValues,
spacesList,
currentSpaceId,
hideOperation,
checkErr,
errMsg,
onValuesChange,
slot,
},
ref,
) => (
<Form<AgentInfoFormValue>
ref={ref}
showValidateIcon={false}
className={classNames(s['upload-form'], className)} // Ensure class name is correct
onValueChange={values => {
onValuesChange(values);
}}
>
<FormInput
initValue={botInputLengthService.sliceStringByMaxLength({
value: initialValues?.name ?? '',
field: 'botName',
})}
field="name"
label={I18n.t('bot_create_name')}
noErrorMessage
maxLength={botInputLengthService.getInputLengthLimit('botName')}
rules={[{ required: true }]}
placeholder={I18n.t('bot_create_name_placeholder')}
getValueLength={reactText =>
botInputLengthService.getValueLength(reactText)
}
/>
{IS_OVERSEA && mode === 'add' ? (
<FormSwitch
field="enableMonetize"
label={I18n.t('monetization')}
desc={I18n.t('monetization_des')}
initValue={true} // Consider if initial value should be prop
rules={[{ required: true }]}
/>
) : null}
<FormTextArea
field="target"
initValue={botInputLengthService.sliceStringByMaxLength({
value: initialValues?.description ?? '',
field: 'botDescription',
})}
label={I18n.t('bot_create_desciption')}
placeholder={I18n.t('bot_create_description_placeholder')}
maxCount={botInputLengthService.getInputLengthLimit('botDescription')}
maxLength={botInputLengthService.getInputLengthLimit('botDescription')}
getValueLength={botInputLengthService.getValueLength}
/>
{showSpace && mode === 'add' ? (
<FormSelect
label={I18n.t('duplicate_select_workspace')}
field="spaceId"
initValue={
hideOperation
? spacesList?.[0]?.id
: (currentSpaceId ?? spacesList?.[0]?.id)
}
placeholder={I18n.t('select_team')}
noErrorMessage
className={classNames(s.select)}
rules={[{ required: true }]}
renderSelectedItem={(optionNode: BotSpace) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Avatar
src={optionNode.icon_url}
size="extra-extra-small"
style={{ flexShrink: 0 }}
>
{optionNode.name}
</Avatar>
<span className={classNames(s['select-name'])}>
{optionNode.name}
</span>
</div>
)}
>
{spacesList
?.filter(t => !t.hide_operation)
?.map(item => (
<FormSelect.Option value={item.id} {...item} key={item.id}>
<div className="ml-[8px]">
{item.icon_url ? (
<Avatar size="extra-small" src={item.icon_url} />
) : (
<IconTeamDefault
className={classNames(s['select-item-icon'])}
/>
)}
</div>
<div className={classNames(s['select-item-name'])}>
<Text
ellipsis={{
showTooltip: false,
}}
style={{
maxWidth: '280px',
}}
>
{item.name}
</Text>
</div>
{item.space_type === SpaceType.Team && (
<Tag color="brand">{I18n.t('develop_team_team')}</Tag>
)}
</FormSelect.Option>
))}
</FormSelect>
) : null}
{slot}
{checkErr ? (
<div className={s['content-check-error']}>
<ReactMarkdown
skipHtml={true}
className={s.markdown}
linkTarget="_blank"
>
{errMsg ?? I18n.t('publish_audit_pop7')}
</ReactMarkdown>
</div>
) : null}
</Form>
),
);

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import cls from 'classnames';
import { Switch, withField, type SwitchProps } from '@coze-arch/coze-design';
function SwitchWithDesc({
value,
onChange,
className,
desc,
descClassName,
switchClassName,
...rest
}: Omit<SwitchProps, 'checked'> & {
value?: boolean;
desc: string;
descClassName?: string;
switchClassName?: string;
}) {
return (
<div className={cls('flex items-center justify-between', className)}>
<span className={cls('coz-fg-primary', descClassName)}>{desc}</span>
<Switch
size="small"
{...rest}
checked={value}
onChange={onChange}
className={cls('shrink-0', switchClassName)}
/>
</div>
);
}
export const FormSwitch = withField(SwitchWithDesc);

View File

@@ -0,0 +1,408 @@
/* stylelint-disable declaration-no-important */
/* stylelint-disable max-nesting-depth */
.card-link {
text-decoration: none;
}
.card {
cursor: pointer;
display: flex;
flex-direction: column;
min-width: 323px;
height: 180px;
padding: 12px 16px 16px 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 6px 8px 0 rgb(28 31 35 / 6%);
&:hover {
box-shadow: 0 10px 12px 0 rgb(28 31 35 / 10%);
}
// 更多操作按钮active时不触发父级active
&:active:not(:focus-within) {
background-color: #e6e7ea;
}
.bot-info {
display: flex;
flex-wrap: nowrap;
margin-bottom: auto;
.bot-avatar {
flex-shrink: 0;
width: 68px;
height: 68px;
margin-top: 12px;
margin-left: 4px;
background-color: #2E2E3814;
border-radius: 8px;
}
.bot-text {
overflow: hidden;
display: flex;
flex: 1;
flex-flow: column nowrap;
margin-left: 16px;
.bot-title {
display: flex;
flex: 1;
justify-content: space-between;
margin-bottom: 4px;
.bot-name {
margin-top: 12px;
font-size: 18px;
font-weight: 600;
line-height: 24px;
color: #1c1d23;
}
}
.bot-description {
height: 40px;
margin-bottom: 8px;
font-size: 14px;
line-height: 20px;
color: rgb(28 29 35 / 80%);
}
.bot-hot-and-category {
display: flex;
align-items: center;
.bot-hot-value {
margin-left: 4px;
font-size: 12px;
font-weight: 400;
color: #1C1D2359;
}
.bot-category-tag {
margin-left: 16px;
border: 1px solid #1D1C2314;
border-radius: 6px;
.bot-category-text {
font-size: 12px;
color: #6B6B75;
}
}
}
.bot-model-and-publish {
display: flex;
align-items: center;
.bot-publish-status {
display: flex;
align-items: center;
font-size: 12px;
line-height: 16px;
color: rgb(28 29 35 / 60%);
letter-spacing: 0.12px;
&-icon {
margin-right: 4px;
color: rgb(62 194 84);
}
&-warning-icon {
margin-right: 4px;
color: var(--semi-color-warning);
}
}
.bot-model {
font-size: 12px;
line-height: 16px;
color: rgb(28 29 35 / 35%);
letter-spacing: 0.12px;
}
.divider {
height: calc(100% - 4px);
}
.bot-publish-platform {
display: flex;
column-gap: 6px;
align-items: center;
font-size: 12px;
line-height: 16px;
color: rgb(28 29 35 / 60%);
letter-spacing: 0.12px;
:global {
.semi-avatar {
width: 12px;
height: 12px;
}
}
}
}
}
}
.bot-creator-info {
overflow: hidden;
display: flex;
align-items: center;
width: 100%;
padding: 0 4px;
font-size: 12px;
line-height: 16px;
color: rgb(28 29 35 / 35%);
letter-spacing: 0.12px;
.creator-avatar {
flex-shrink: 0;
width: 16px;
height: 16px;
margin-right: 4px;
}
.bot-creator {
font-size: 12px;
line-height: 16px;
color: rgb(28 29 35 / 35%);
letter-spacing: 0.12px;
}
.bot-edit-time {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: flex-end;
margin-left: auto;
}
}
}
.explore-height {
height: 136px;
}
.publish-popover {
&-item {
display: flex;
align-items: center;
padding: 4px;
&:first-child {
padding-top: 0;
}
&:last-child {
padding-bottom: 0;
}
&:not(:last-child) {
border-bottom: 1px solid #efefef;
}
&-avatar {
margin-right: 10px;
}
:global {
img {
width: 100%;
height: 100%;
}
}
}
}
.bot-description-tooltip {
color: var(--semi-color-bg-0);
}
.add-card {
background-color: white;
border-radius: 8px;
}
.add-card-inner {
display: flex;
flex-direction: column;
justify-content: center;
}
.name-wrap {
display: flex;
align-items: center;
width: 100%;
height: 40px;
padding: 16px 16px 0;
}
.name {
position: absolute;
top: 16px;
left: 48px;
overflow: hidden;
max-width: calc(100% - 156px);
font-size: 14px;
font-weight: 600;
line-height: 24px;
color: #000;
text-overflow: ellipsis;
white-space: nowrap;
}
.extra {
margin-left: auto;
}
.description {
overflow: hidden;
display: -webkit-box;
font-size: 12px;
line-height: 18px;
color: #494c4f;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.recent-modify {
margin-top: 8px;
font-size: 12px;
line-height: 16px;
color: rgb(28 31 35 / 60%);
}
.creator {
width: fit-content;
padding: 4px;
font-size: 12px;
line-height: 16px;
color: #346ef8;
background: rgb(51 112 255 / 10%);
border: none !important;
border-radius: 3px !important;
}
.upload-form {
.upload-filed {
padding-top: 0;
}
.textarea-multi-line {
:global {
.semi-input-textarea {
padding-right: 66px;
}
.semi-input-textarea-counter {
position: absolute;
right: 12px;
bottom: 8px;
min-height: 0;
padding: 0;
color: var(--light-usage-text-color-text-3, rgb(28 29 35 / 35%));
}
}
}
:global {
.semi-form-field-label {
margin-bottom: 8px;
}
}
}
.upload-form-item {
:global {
.semi-form-field-label {
display: none;
}
.semi-form-field-label-text {
display: none;
}
.semi-form-field {
padding: 0;
}
}
}
.collect-num {
width: 12px;
height: 12px;
margin-right: 4px;
svg {
width: 12px;
height: 12px;
}
}
.user-info {
margin-top: auto;
}
.content-check-error {
margin-top: -8px;
color: red;
}
.bot-ui-modal {
:global {
.semi-modal-content .semi-modal-body {
padding: 12px 0 22px;
}
.semi-form-vertical .semi-form-field {
padding-bottom: 14px !important;
}
}
}
.select {
width: 100%;
.select-name {
margin-left: 6px;
font-weight: 400;
}
}
.select-item-icon {
svg {
width: 24px;
height: 24px;
}
}
.select-item-name {
margin-right: 16px;
margin-left: 12px;
font-weight: 600;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
useAgentFormManagement,
type UseAgentFormManagementProps,
} from './use-agent-form-management';
export {
useAgentPersistence,
type UseAgentPersistenceProps,
} from './use-agent-persistence';
export {
AgentInfoForm,
type AgentInfoFormProps,
type AgentInfoFormValue,
} from './agent-info-form';

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useRef } from 'react';
import { type Form } from '@coze-arch/coze-design';
import { type DraftBot } from '@coze-arch/bot-api/developer_api';
import { type AgentInfoFormValue } from './agent-info-form';
export interface UseAgentFormManagementProps {
initialBotInfo?: DraftBot;
}
export const useAgentFormManagement = ({
initialBotInfo,
}: UseAgentFormManagementProps) => {
const formRef = useRef<Form<AgentInfoFormValue>>(null);
const [isOkButtonDisable, setOkButtonDisable] = useState(
!initialBotInfo?.name?.trim(),
);
const [botInfo4Generate, setBotInfo4Generate] = useState<{
name: string;
desc: string;
avatar: { uri: string; url: string };
}>({
name: initialBotInfo?.name || '',
desc: initialBotInfo?.description || '',
avatar: {
uri: initialBotInfo?.icon_uri || '',
url: initialBotInfo?.icon_url || '',
},
});
const [checkErr, setCheckErr] = useState(false);
const [errMsg, setErrMsg] = useState('');
const [confirmDisabled, setConfirmDisabled] = useState(false);
const resetFormState = () => {
setOkButtonDisable(!initialBotInfo?.name?.trim());
setBotInfo4Generate({
name: initialBotInfo?.name || '',
desc: initialBotInfo?.description || '',
avatar: {
uri: initialBotInfo?.icon_uri || '',
url: initialBotInfo?.icon_url || '',
},
});
setCheckErr(false);
setErrMsg('');
};
const handleFormValuesChange = (values: AgentInfoFormValue) => {
setBotInfo4Generate({
name: values.name?.trim() || '',
desc: values.target?.trim() || '',
avatar: {
uri: values.bot_uri?.[0]?.uid || '',
url: values.bot_uri?.[0]?.url || '',
},
});
setCheckErr(false);
setErrMsg('');
setOkButtonDisable(!values.name?.trim());
};
const getValues = async () => {
const formApi = formRef.current?.formApi;
await formApi?.validate();
return formApi?.getValues();
};
const setBotIcon = (val: { url: string; uid: string }) => {
const formApi = formRef.current?.formApi;
formApi?.setValue('bot_uri', [val]);
};
return {
formRef,
isOkButtonDisable,
botInfo4Generate,
checkErr,
errMsg,
confirmDisabled,
setCheckErr,
setErrMsg,
setConfirmDisabled,
setOkButtonDisable,
handleFormValuesChange,
getValues,
setBotIcon,
resetFormState,
};
};

View File

@@ -0,0 +1,274 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable complexity */
import { useState } from 'react';
import { withSlardarIdButton } from '@coze-studio/bot-utils';
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
import { useCollaborationStore } from '@coze-studio/bot-detail-store/collaboration';
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
import { updateBotRequest } from '@coze-studio/bot-detail-store';
import {
REPORT_EVENTS as ReportEventNames,
createReportEvent,
} from '@coze-arch/report-events';
import { logger } from '@coze-arch/logger';
import { I18n } from '@coze-arch/i18n';
import { Toast } from '@coze-arch/coze-design';
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
import { useSpaceStore } from '@coze-arch/bot-studio-store';
import { CustomError } from '@coze-arch/bot-error';
import {
type BotSpace,
SpaceType,
type DraftBotCreateResponse,
} from '@coze-arch/bot-api/developer_api';
import { DeveloperApi } from '@coze-arch/bot-api';
import { type AgentInfoFormValue } from './agent-info-form';
type OnSuccessCallback = (
botId?: string,
spaceId?: string,
extra?: { botName?: string; botAvatar?: string; botDesc?: string },
) => void | Promise<void>;
export interface UseAgentPersistenceProps {
mode: 'add' | 'update';
botId?: string; // Current bot ID for update mode
currentSpaceId?: string; // Current space ID from store
outerSpaceId?: string; // Space ID passed via props
getValues: () => Promise<AgentInfoFormValue | undefined>;
onSuccess?: OnSuccessCallback;
onError?: () => void;
onBefore?: () => void;
setVisible: (visible: boolean) => void;
setCheckErr: (isError: boolean) => void;
setErrMsg: (message: string) => void;
bizCreateFrom?: 'navi' | 'space';
showSpace?: boolean;
}
// eslint-disable-next-line max-lines-per-function, @coze-arch/max-line-per-function
export const useAgentPersistence = ({
mode,
botId,
currentSpaceId,
outerSpaceId,
getValues,
onSuccess,
onError,
onBefore,
setVisible,
setCheckErr,
setErrMsg,
bizCreateFrom,
showSpace,
}: UseAgentPersistenceProps) => {
const [loading, setLoading] = useState(false);
const setBotInfoByImmer = useBotInfoStore(state => state.setBotInfoByImmer);
const setCollaborationByImmer = useCollaborationStore(
state => state.setCollaborationByImmer,
);
const setPageRuntimeByImmer = usePageRuntimeStore(
state => state.setPageRuntimeByImmer,
);
const {
spaces: { bot_space_list: list },
} = useSpaceStore();
const reportEvent = createReportEvent({
eventName:
mode === 'add' ? ReportEventNames.createBot : ReportEventNames.updateBot,
});
const reportTea = ({
resp,
values,
personalSpaceInfo,
paramsSpaceId,
}: {
resp: DraftBotCreateResponse;
values: AgentInfoFormValue | undefined;
personalSpaceInfo: BotSpace | undefined;
paramsSpaceId: string;
}) => {
if (resp.code === 0) {
sendTeaEvent(EVENT_NAMES.create_bot_result, {
source: showSpace ? 'menu_bar' : 'bot_list',
workspace_type:
personalSpaceInfo?.id === paramsSpaceId
? 'personal_workspace'
: 'team_workspace',
result: 'success',
bot_name: values?.name || '',
bot_desc: values?.target || '',
});
} else {
sendTeaEvent(EVENT_NAMES.create_bot_result, {
source: showSpace ? 'menu_bar' : 'bot_list',
workspace_type:
personalSpaceInfo?.id === paramsSpaceId
? 'personal_workspace'
: 'team_workspace',
result: 'failed',
error_code: resp.code,
error_message: resp.msg,
bot_name: values?.name || '',
bot_desc: values?.target || '',
});
}
};
const handleUpdateBot = async () => {
if (!botId) {
const msg = I18n.t('bot_copy_id_error');
throw new CustomError(ReportEventNames.updateBot, msg);
}
const values = await getValues();
logger.info({ message: 'update values', meta: { values } });
try {
setLoading(true);
const botBaseInfo = {
icon_uri: values?.bot_uri?.[0].uid || '',
name: values?.name,
description: values?.target ? values.target : '',
};
const { data } = await updateBotRequest(botBaseInfo);
if (data.check_not_pass) {
setCheckErr(true);
setErrMsg(data.check_not_pass_msg);
onError?.();
return;
}
setBotInfoByImmer(store => {
store.icon_uri = values?.bot_uri?.[0].uid;
store.icon_url = values?.bot_uri?.[0].url;
store.name = values?.name;
store.description = values?.target;
});
setCollaborationByImmer(store => {
store.sameWithOnline = data.same_with_online ?? false;
});
setPageRuntimeByImmer(store => {
store.hasUnpublishChange = data.has_change ?? false;
});
await onSuccess?.(botId, currentSpaceId, {
botAvatar: values?.bot_uri?.[0].url,
botName: values?.name,
botDesc: values?.target,
});
setVisible(false);
reportEvent.success();
Toast.success({
content: I18n.t('Update_success'),
showClose: false,
});
} catch (e) {
if (e instanceof Error) {
reportEvent.error({ error: e, reason: e.message });
}
onError?.();
Toast.error({
content: withSlardarIdButton(I18n.t('Update_failed')),
showClose: false,
});
throw e;
} finally {
setLoading(false);
}
};
const handleCreateBot = async () => {
const values = await getValues();
setLoading(true);
const paramsSpaceId =
values?.spaceId || outerSpaceId || currentSpaceId || list?.[0]?.id || '';
const personalSpaceInfo = list?.find(
item => item.space_type === SpaceType.Personal,
);
try {
onBefore?.();
const resp = await DeveloperApi.DraftBotCreate({
name: values?.name,
description: values?.target,
icon_uri: values?.bot_uri?.[0]?.uid,
space_id: paramsSpaceId,
...(IS_OVERSEA && {
monetization_conf: { is_enable: values?.enableMonetize },
}),
create_from: bizCreateFrom,
});
if (resp.data.check_not_pass) {
setCheckErr(true);
setErrMsg(resp.data.check_not_pass_msg);
onError?.();
return;
}
Toast.success({
content: I18n.t('bot_created_toast'),
showClose: false,
});
// 兼容 onSuccess 回调为同步函数的场景
await onSuccess?.(resp.data?.bot_id, paramsSpaceId, {
botName: values?.name,
botDesc: values?.target,
botAvatar: values?.bot_uri?.[0]?.url,
});
sendTeaEvent(EVENT_NAMES.click_create_bot_confirm, {
click: 'success',
bot_id: resp.data?.bot_id,
create_type: 'create',
});
reportTea({ resp, values, personalSpaceInfo, paramsSpaceId });
reportEvent.success();
setVisible(false);
return resp;
} catch (e) {
Toast.error({
content: withSlardarIdButton(I18n.t('Create_failed')),
showClose: false,
});
if (e instanceof Error) {
reportEvent.error({ error: e, reason: e.message });
sendTeaEvent(EVENT_NAMES.click_create_bot_confirm, {
click: 'failed',
create_type: 'create',
error_message: e.message,
});
}
onError?.();
// 阻止弹窗关闭
throw e;
} finally {
setLoading(false);
}
};
return {
loading,
handleCreateBot,
handleUpdateBot,
};
};