chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -75,7 +75,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
state => state.space.space_type === SpaceType.Personal,
|
||||
);
|
||||
|
||||
// 关键字检索 & 筛选
|
||||
// Keyword Search & Filtering
|
||||
const [filterParams, setFilterParams, debouncedSetSearchValue] =
|
||||
useCachedQueryParams();
|
||||
|
||||
@@ -98,7 +98,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
hasPublished: getPublishRequestParam(filterParams.isPublish),
|
||||
recentlyOpen: filterParams.recentlyOpen,
|
||||
searchScope: filterParams.searchScope,
|
||||
// 固定值,来自历史代码
|
||||
// Fixed value, from historical code
|
||||
orderBy: filterParams.isPublish
|
||||
? search.OrderBy.PublishTime
|
||||
: search.OrderBy.UpdateTime,
|
||||
@@ -134,7 +134,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建 project
|
||||
* Create project
|
||||
*/
|
||||
const { contextHolder, actions } = useIntelligenceActions({
|
||||
spaceId,
|
||||
@@ -172,7 +172,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
val as (typeof TYPE_FILTER_OPTIONS)[number]['value'],
|
||||
}));
|
||||
|
||||
// tea 埋点
|
||||
// Tea event tracking
|
||||
sendTeaEvent(EVENT_NAMES.workspace_action_front, {
|
||||
space_id: spaceId,
|
||||
space_type: isPersonal ? 'personal' : 'teamspace',
|
||||
@@ -195,8 +195,8 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
{!isPersonal ? (
|
||||
/**
|
||||
* Search Scope
|
||||
* 所有人
|
||||
* 由我创建
|
||||
* Everybody.
|
||||
* Created by me
|
||||
*/
|
||||
<Select
|
||||
className="min-w-[128px]"
|
||||
@@ -220,7 +220,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
searchScope: val,
|
||||
};
|
||||
});
|
||||
// tea 埋点
|
||||
// Tea event tracking
|
||||
sendTeaEvent(EVENT_NAMES.workspace_action_front, {
|
||||
space_id: spaceId,
|
||||
space_type: isPersonal ? 'personal' : 'teamspace',
|
||||
@@ -242,9 +242,9 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
</Select>
|
||||
) : null}
|
||||
{/*
|
||||
全部
|
||||
已发布
|
||||
最近打开
|
||||
all
|
||||
Published
|
||||
Recently opened
|
||||
*/}
|
||||
<Select
|
||||
className="min-w-[128px]"
|
||||
@@ -265,7 +265,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
? val
|
||||
: DevelopCustomPublishStatus.All,
|
||||
}));
|
||||
// tea 埋点
|
||||
// Tea event tracking
|
||||
sendTeaEvent(EVENT_NAMES.workspace_action_front, {
|
||||
space_id: spaceId,
|
||||
space_type: isPersonal ? 'personal' : 'teamspace',
|
||||
@@ -302,7 +302,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
</SubHeader>
|
||||
<Content ref={containerRef}>
|
||||
<Spin spinning={loading} wrapperClassName="w-full !h-[80vh]">
|
||||
{/* 有数据时 */}
|
||||
{/* When data is available */}
|
||||
{data?.list.length ? (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -378,7 +378,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* 展示底部的 loading */}
|
||||
{/* Show loading at the bottom. */}
|
||||
{data?.list.length && loadingMore ? (
|
||||
<div className="flex items-center justify-center w-full h-[38px] my-[20px] coz-fg-secondary text-[12px]">
|
||||
<IconButton
|
||||
@@ -389,7 +389,7 @@ export const Develop: FC<DevelopProps> = ({ spaceId }) => {
|
||||
<div>{I18n.t('Loading')}...</div>
|
||||
</div>
|
||||
) : null}
|
||||
{/* 没有更多数据的时候要展示个占位 */}
|
||||
{/* Show a placeholder when there is no more data */}
|
||||
{noMore && data?.list.length ? (
|
||||
<div className="h-[38px] my-[20px]"></div>
|
||||
) : null}
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface CreatorProps {
|
||||
|
||||
export const Creator: FC<CreatorProps> = ({ avatar, name, extra }) => (
|
||||
<div className="flex items-center gap-x-[4px] h-[16px] coz-fg-secondary text-[12px] leading-16px">
|
||||
{/* 开源版无多人协作功能,不展示资源所有者信息 */}
|
||||
{/* The open-source version has no multi-person collaboration function and does not display resource owner information */}
|
||||
{IS_OPEN_SOURCE ? null : (
|
||||
<>
|
||||
<Avatar className="w-[16px] h-[16px] flex-shrink-0" src={avatar} />
|
||||
|
||||
@@ -21,8 +21,8 @@ import { IconCozEmpty, IconCozBroom } from '@coze-arch/coze-design/icons';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
interface WorkspaceEmptyProps {
|
||||
onClear?: () => void; // 清空按钮点击事件
|
||||
hasFilter?: boolean; // 是否有筛选项
|
||||
onClear?: () => void; // Clear button click event
|
||||
hasFilter?: boolean; // Is there a filter
|
||||
}
|
||||
|
||||
export const WorkspaceEmpty: FC<WorkspaceEmptyProps> = ({
|
||||
|
||||
@@ -19,7 +19,7 @@ export { default as Tool } from './pages/tool';
|
||||
export { default as MocksetDetail } from './pages/mockset';
|
||||
export { default as MocksetList } from './pages/mockset-list';
|
||||
|
||||
// !Notice 禁止直接导出 knowledge 相关代码,避免首屏加载
|
||||
// ! Notice prohibits the direct export of knowledge-related codes to avoid first-screen loading
|
||||
// export { default as KnowledgePreviewPage } from './pages/knowledge-preview';
|
||||
// export { default as KnowledgeUploadPage } from './pages/knowledge-upload';
|
||||
export { default as DatabaseDetailPage } from './pages/database';
|
||||
@@ -29,7 +29,7 @@ export {
|
||||
compareObjects,
|
||||
} from './utils';
|
||||
|
||||
// 公共组件
|
||||
// common component
|
||||
export { Creator } from './components/creator';
|
||||
export {
|
||||
Content,
|
||||
|
||||
@@ -53,7 +53,7 @@ export interface BotCardProps {
|
||||
intelligenceInfo: IntelligenceData;
|
||||
timePrefixType?: 'recentOpen' | 'publish' | 'edit';
|
||||
/**
|
||||
* 返回 true 时会打断默认的跳转行为
|
||||
* Returning true interrupts the default jump behavior
|
||||
*/
|
||||
onClick?: (() => true) | (() => void);
|
||||
onDelete?: (param: {
|
||||
@@ -128,7 +128,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
};
|
||||
|
||||
if (!id || !space_id) {
|
||||
// id 和 space id 对 bot 卡片来说是必须的,这里约束一下 ts 类型
|
||||
// The id and space id are necessary for the bot card. Here are the constraints on the ts type
|
||||
throw Error('No botID or no spaceID which are necessary');
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
return formatDate(Number(timestamp), getFormatDateType(Number(timestamp)));
|
||||
}, [timePrefixType, publish_time, update_time, recently_open_time]);
|
||||
|
||||
// 是否展示 card 复层操作按钮
|
||||
// Whether to display the card layering operation button
|
||||
const [showActions, setShowActions] = useState(false);
|
||||
// 是否展示 menu 菜单,这里有其他组件主动调用,需要受控
|
||||
// Whether to display the menu menu, there are other components actively calling here, which need to be controlled
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -211,20 +211,20 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
}}
|
||||
data-testid="bot-list-page.bot-card"
|
||||
>
|
||||
{/* 展示迁移失败状态 icon */}
|
||||
{/* Display migration failure status icon */}
|
||||
{statusExtra}
|
||||
|
||||
{/* bot 基本信息 */}
|
||||
{/* Bot basic information */}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-[4px] w-[calc(100%-76px)]">
|
||||
<div className="flex items-center gap-[4px]">
|
||||
<Name name={name} />
|
||||
{isBanned ? (
|
||||
// 如果失效了,高优展示失效 icon
|
||||
// If it fails, display the failure icon
|
||||
<IconCozWarningCircleFill className="text-xxl coz-fg-hglt-red flex-shrink-0" />
|
||||
) : (
|
||||
<>
|
||||
{/* 发布状态 icon */}
|
||||
{/* Publish status icon */}
|
||||
{renderPublishStatusIcon()}
|
||||
{headerExtra}
|
||||
</>
|
||||
@@ -240,10 +240,10 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 项目/智能体 */}
|
||||
{/* Projects/Agents */}
|
||||
<IntelligenceTag intelligenceType={type} />
|
||||
|
||||
{/* bot 作者信息 */}
|
||||
{/* Bot author information */}
|
||||
{!!owner_info && (
|
||||
<Creator
|
||||
avatar={owner_info.avatar_url}
|
||||
@@ -252,7 +252,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* actions 浮层 action 浮层出现的时候下方有一个白色遮罩 */}
|
||||
{/* Actions Floating layer action When the floating layer appears, there is a white mask below */}
|
||||
{!hideOperation ? (
|
||||
<>
|
||||
{showActions && actionsMenuVisible ? (
|
||||
@@ -267,14 +267,14 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
<div
|
||||
className="absolute bottom-[16px] right-[16px] flex gap-[4px]"
|
||||
onClick={e => {
|
||||
// 阻止 click 事件冒泡到 card 最外层
|
||||
// Prevent click events from bubbling to the outermost layer of the card
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{showActions && actionsMenuVisible ? (
|
||||
<>
|
||||
{!isBanned ? (
|
||||
// 收藏 bot
|
||||
// Favorite bot
|
||||
<FavoriteIconBtn
|
||||
useButton
|
||||
isVisible
|
||||
@@ -295,7 +295,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{/* 下拉菜单 */}
|
||||
{/* dropdown menu */}
|
||||
<Menu
|
||||
keepDOM
|
||||
className="w-fit mt-4px mb-4px"
|
||||
@@ -304,7 +304,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
visible={showMenu}
|
||||
render={
|
||||
<Menu.SubMenu mode="menu">
|
||||
{/* 复制 bot */}
|
||||
{/* Copy bot */}
|
||||
{isAgent ? (
|
||||
<MenuCopyBot
|
||||
id={id}
|
||||
@@ -330,7 +330,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{extraMenu}
|
||||
{/* 删除 bot */}
|
||||
{/* Delete bot */}
|
||||
<Tooltip
|
||||
position="left"
|
||||
trigger={can_delete ? 'custom' : 'hover'}
|
||||
@@ -365,7 +365,7 @@ export const BotCard: React.FC<BotCardProps> = ({
|
||||
</Menu>
|
||||
</>
|
||||
) : is_fav && !isBanned ? (
|
||||
// 如果 bot 已经收藏了,非 hover 时展示 icon
|
||||
// If the bot has already been collected, display the icon when not hovering.
|
||||
<IconButton
|
||||
className="!pt-[20px]"
|
||||
color="secondary"
|
||||
|
||||
@@ -41,7 +41,7 @@ export const IntelligenceTag: React.FC<IntelligenceTagProps> = ({
|
||||
if (intelligenceType === IntelligenceType.DouyinAvatarBot) {
|
||||
return (
|
||||
<Tag color="red" size="small" className="w-fit">
|
||||
{/* TODO: i18n 文案 */}
|
||||
{/* TODO: i18n copywriting */}
|
||||
抖音分身
|
||||
</Tag>
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ export const useCachedQueryParams = () => {
|
||||
);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
/** 当筛选条件变化时,取合适的 key 存入本地 */
|
||||
/** When the filter conditions change, take the appropriate key and store it locally */
|
||||
const { searchScope, isPublish, recentlyOpen, searchType } = filterParams;
|
||||
localStorageService.setValue(
|
||||
'workspace-develop-filters',
|
||||
@@ -63,7 +63,7 @@ export const useCachedQueryParams = () => {
|
||||
}, [filterParams]);
|
||||
|
||||
useEffect(() => {
|
||||
/** 异步读取本地存储的筛选条件 */
|
||||
/** Asynchronously reads filters from local storage */
|
||||
getDefaultFilterParams().then(filters => {
|
||||
setFilterParams(prev => merge({}, prev, filters));
|
||||
});
|
||||
@@ -75,7 +75,7 @@ export const useCachedQueryParams = () => {
|
||||
...params,
|
||||
searchValue,
|
||||
}));
|
||||
// tea 埋点
|
||||
// Tea event tracking
|
||||
sendTeaEvent(EVENT_NAMES.search_front, {
|
||||
full_url: location.href,
|
||||
source: 'develop',
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useGlobalEventListeners = ({
|
||||
const handlerRefreshFavList = (
|
||||
refreshFavListParams: RefreshFavListParams,
|
||||
) => {
|
||||
// 只在工作空间收藏取消收藏变化的时候刷新列表
|
||||
// Refresh the list only when the workspace collection is cancelled and the collection is changed.
|
||||
if (refreshFavListParams.emitPosition === 'favorites-list-item') {
|
||||
reload();
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ const getIntelligenceList = async (
|
||||
}: FilterParamsType,
|
||||
cancelTokenRef: React.MutableRefObject<CancelTokenSource | null>,
|
||||
) => {
|
||||
// 每次新的请求,都重置一下 cancel token
|
||||
// Reset the cancel token every time a new request is made.
|
||||
const source = axios.CancelToken.source();
|
||||
cancelTokenRef.current = source;
|
||||
const resp = await intelligenceApi
|
||||
@@ -76,7 +76,7 @@ const getIntelligenceList = async (
|
||||
recently_open: recentlyOpen,
|
||||
cursor_id: dataSource?.nextCursorId,
|
||||
search_scope: searchScope,
|
||||
// 固定值,来自历史代码
|
||||
// Fixed value, from historical code
|
||||
order_by: orderBy,
|
||||
status: [
|
||||
IntelligenceStatus.Using,
|
||||
@@ -193,7 +193,7 @@ export const useIntelligenceList = ({
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// 取消正在请求的接口
|
||||
// Cancel the requested interface
|
||||
cancelTokenRef.current?.cancel();
|
||||
},
|
||||
[spaceId],
|
||||
|
||||
@@ -84,7 +84,7 @@ export const useProjectCopyPolling = ({
|
||||
});
|
||||
}),
|
||||
);
|
||||
// 需要重新封装下
|
||||
// Need to be re-packaged
|
||||
list.forEach(item => {
|
||||
if (item.entity_status === IntelligenceStatus.Using) {
|
||||
const successToastId = Toast.success({
|
||||
|
||||
@@ -32,7 +32,7 @@ export const produceCopyIntelligenceData = ({
|
||||
basicInfo: IntelligenceBasicInfo;
|
||||
};
|
||||
}) => {
|
||||
// 这是 fallback
|
||||
// This is fallback
|
||||
const userInfo = getUserInfo();
|
||||
const userLabel = getUserLabel();
|
||||
return produce<IntelligenceData>(originTemplateData, draft => {
|
||||
|
||||
@@ -31,8 +31,8 @@ export const getPublishRequestParam = (
|
||||
};
|
||||
|
||||
/**
|
||||
* 项目类型请求前后端参数映射,将DevelopCustomTypeStatus映射为IntelligenceType[]
|
||||
* 需要根据是否可以展示抖音分身来决定是否处理 DouyinAvatarBot
|
||||
* Project Type Request Front and Back End Parameter Mapping, Mapping DevelopCustomTypeStatus to Intelligence Type []
|
||||
* You need to decide whether to deal with DouyinAvatarBot according to whether the Douyin doppelganger can be displayed.
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,7 @@ export enum DevelopCustomTypeStatus {
|
||||
All = 0,
|
||||
Project = 1,
|
||||
Agent = 2,
|
||||
DouyinAvatarBot = 3, // single agent 类型的抖音分身
|
||||
DouyinAvatarBot = 3, // Single agent type Douyin doppelganger
|
||||
}
|
||||
|
||||
export interface DraftIntelligenceList {
|
||||
|
||||
@@ -83,7 +83,7 @@ export const KnowledgePreviewPage = () => {
|
||||
if (params.biz === 'project') {
|
||||
return <BizProjectKnowledgeIDE />;
|
||||
}
|
||||
// 默认'library'
|
||||
// Default'library'
|
||||
return <BizLibraryKnowledgeIDE />;
|
||||
})()}
|
||||
</KnowledgeParamsStoreProvider>
|
||||
|
||||
@@ -33,9 +33,9 @@ export const initialParam: QueryParams = {
|
||||
name: '',
|
||||
};
|
||||
|
||||
/** 是否由当前用户创建:
|
||||
* 0-不筛选
|
||||
* 1-当前用户 */
|
||||
/** Is it created by the current user?
|
||||
* 0 - Do not filter
|
||||
* 1 - Current user */
|
||||
export const getScopeOptions = () => [
|
||||
{
|
||||
label: I18n.t('library_filter_tags_all_creators'),
|
||||
@@ -47,10 +47,10 @@ export const getScopeOptions = () => [
|
||||
},
|
||||
];
|
||||
|
||||
/** 发布状态:
|
||||
* 0-不筛选
|
||||
* 1-未发布
|
||||
* 2-已发布 */
|
||||
/** Release status:
|
||||
* 0 - Do not filter
|
||||
* 1 - Unpublished
|
||||
* 2- Published */
|
||||
export const getStatusOptions = () => [
|
||||
{
|
||||
label: I18n.t('library_filter_tags_all_status'),
|
||||
|
||||
@@ -27,7 +27,7 @@ import { compareObjects } from '@/utils';
|
||||
import { initialParam, type QueryParams } from '../consts';
|
||||
|
||||
/**
|
||||
* 从url query中获取搜索参数 优先级最高,高于LS缓存
|
||||
* Get search parameters from url query, highest priority, higher than LS cache
|
||||
* @returns {} | undefined
|
||||
*/
|
||||
const getSearchParamsFromUrl = () => {
|
||||
@@ -47,7 +47,7 @@ const getSearchParamsFromUrl = () => {
|
||||
return searchParams;
|
||||
};
|
||||
|
||||
// 异步初始化获取筛选参数, 分别从LS缓存获取和url query获取
|
||||
// Asynchronous initialization to obtain filter parameters, obtained from LS cache and url query respectively
|
||||
const getDefaultFilterParams = async () => {
|
||||
const searchParamsFromUrl = getSearchParamsFromUrl();
|
||||
const localFilterParams = await localStorageService.getValueSync(
|
||||
@@ -60,7 +60,7 @@ const getDefaultFilterParams = async () => {
|
||||
defaultFilterParams = { ...defaultFilterParams, ...safeParams };
|
||||
}
|
||||
|
||||
// 图像流和工作流合并会删除资源中的图像流选项 这里转换成全部
|
||||
// Merging image flow and workflow removes the image flow option in the resource, here converted to all
|
||||
if (defaultFilterParams?.res_type_filter?.[0] === 3) {
|
||||
defaultFilterParams.res_type_filter[0] = -1;
|
||||
}
|
||||
@@ -80,21 +80,21 @@ export const useCachedQueryParams = ({ spaceId }: { spaceId: string }) => {
|
||||
'name',
|
||||
]);
|
||||
|
||||
/** 每次切换空间的时候,重新初始化筛选条件,并清空搜索框,重新请求资源列表 */
|
||||
/** Each time you switch spaces, reinitialize the filter criteria, clear the search box, and rerequest the resource list */
|
||||
useEffect(() => {
|
||||
setReady(false);
|
||||
getDefaultFilterParams().then(filters => {
|
||||
setParams(p => ({
|
||||
...p,
|
||||
...filters,
|
||||
cursor: '', // 筛选、刷新时重置为空
|
||||
cursor: '', // Filter, reset to empty when refreshing
|
||||
}));
|
||||
setReady(true);
|
||||
});
|
||||
}, [spaceId]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
/** 当筛选条件变化时,取合适的 key 存入本地 */
|
||||
/** When the filter conditions change, take the appropriate key and store it locally */
|
||||
const tempParams = {
|
||||
res_type_filter: params.res_type_filter,
|
||||
user_filter: params.user_filter,
|
||||
|
||||
@@ -35,7 +35,7 @@ import { BaseLibraryItem } from '../components/base-library-item';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
// 预设表格cell最小宽度
|
||||
// Default table cell minimum width
|
||||
const NAME_COL_WIDTH = 260;
|
||||
const ACTIONS_COL_WIDTH = 60;
|
||||
const TYPE_COL_WIDTH = 100;
|
||||
@@ -43,7 +43,7 @@ const CREATOR_COL_WIDTH = 231;
|
||||
const EDITED_TIME_COL_WIDTH = 150;
|
||||
|
||||
const stopPro = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation(); //阻止冒泡
|
||||
e.stopPropagation(); //Stop bubbling
|
||||
};
|
||||
|
||||
const getResTypeLabelFromConfigMap = (
|
||||
|
||||
@@ -90,11 +90,11 @@ export const useDatabaseConfig: UseEntityConfigHook = ({
|
||||
);
|
||||
},
|
||||
renderActions: (item: ResourceInfo) => {
|
||||
// 是否能删除
|
||||
// Can it be deleted?
|
||||
const deleteDisabled = !item.actions?.find(
|
||||
action => action.key === ActionKey.Delete,
|
||||
)?.enable;
|
||||
// 是否启用
|
||||
// Whether to enable
|
||||
|
||||
// delete operation
|
||||
const deleteProps = {
|
||||
|
||||
@@ -36,7 +36,7 @@ import { type UseEntityConfigHook } from './types';
|
||||
|
||||
const { TableAction } = Table;
|
||||
/**
|
||||
* 知识库tag:
|
||||
* Knowledge base tags:
|
||||
* 0-text
|
||||
* 1-table
|
||||
* 2-image
|
||||
@@ -48,7 +48,7 @@ const knowledgeSubTypeTextMap: Record<number, I18nKeysNoOptionsType> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 禁用状态tag:
|
||||
* Disable status tag:
|
||||
* 3-disabled
|
||||
* */
|
||||
const knowledgeBizStatusTextMap: Record<number, I18nKeysNoOptionsType> = {
|
||||
@@ -172,7 +172,7 @@ export const useKnowledgeConfig: UseEntityConfigHook = ({
|
||||
},
|
||||
});
|
||||
|
||||
// 删除
|
||||
// delete
|
||||
const { run: delKnowledge } = useRequest(
|
||||
(datasetId: string) =>
|
||||
KnowledgeApi.DeleteDataset({
|
||||
@@ -187,14 +187,14 @@ export const useKnowledgeConfig: UseEntityConfigHook = ({
|
||||
},
|
||||
);
|
||||
|
||||
// 开启开关
|
||||
// turn on switch
|
||||
const { run: enableKnowledge, loading } = useRequest(
|
||||
(enableStatus: boolean, record: ResourceInfo) =>
|
||||
KnowledgeApi.UpdateDataset({
|
||||
dataset_id: record.res_id,
|
||||
name: record.name,
|
||||
description: record.desc,
|
||||
icon_uri: record.biz_extend?.icon_uri, // 从业务字段获取
|
||||
icon_uri: record.biz_extend?.icon_uri, // Get from business field
|
||||
status: enableStatus
|
||||
? DatasetStatus.DatasetReady
|
||||
: DatasetStatus.DatasetForbid,
|
||||
@@ -228,7 +228,7 @@ export const useKnowledgeConfig: UseEntityConfigHook = ({
|
||||
const deleteDisabled = !item.actions?.find(
|
||||
action => action.key === ActionKey.Delete,
|
||||
)?.enable;
|
||||
// knowledge 资源启用状态开关 enable 的是否禁用状态(即switch标签的禁用状态)
|
||||
// Knowledge Resource Enabled Status Whether the switch enabled is disabled (i.e. the disabled state of the switch label)
|
||||
const enableDisabled = !item.actions?.find(
|
||||
action => action.key === ActionKey.EnableSwitch,
|
||||
)?.enable;
|
||||
|
||||
@@ -75,7 +75,7 @@ export const usePromptConfig: UseEntityConfigHook = ({
|
||||
usePromptConfiguratorModal({
|
||||
spaceId,
|
||||
source: 'resource_library',
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
enableDiff: FLAGS['bot.studio.prompt_diff'],
|
||||
onUpdateSuccess: reloadList,
|
||||
onDiff: ({ libraryId }) => {
|
||||
@@ -86,7 +86,7 @@ export const usePromptConfig: UseEntityConfigHook = ({
|
||||
},
|
||||
});
|
||||
|
||||
// 删除
|
||||
// delete
|
||||
const { run: delPrompt } = useRequest(
|
||||
(promptId: string) =>
|
||||
PlaygroundApi.DeletePromptResource({
|
||||
|
||||
@@ -57,7 +57,7 @@ export const useWorkflowConfig: UseEntityConfigHook = ({
|
||||
value: ResType.Workflow,
|
||||
},
|
||||
parseParams: params => {
|
||||
// 工作流图像流合并之后 选中工作流需要同时也拉取出图像流
|
||||
// After the workflow image stream is merged, the selected workflow needs to also pull out the image stream
|
||||
if (params?.res_type_filter?.[0] === ResType.Workflow) {
|
||||
return {
|
||||
...params,
|
||||
@@ -77,7 +77,7 @@ export const useWorkflowConfig: UseEntityConfigHook = ({
|
||||
>
|
||||
{I18n.t('library_resource_type_workflow')}
|
||||
</Menu.Item>
|
||||
{/* 开源版本暂时不支持对话流 */}
|
||||
{/* The open-source version does not support conversation streaming for the time being */}
|
||||
{!IS_OPEN_SOURCE ? (
|
||||
<Menu.Item
|
||||
data-testid="workspace.library.header.create.chatflow"
|
||||
|
||||
@@ -81,7 +81,7 @@ export const BaseLibraryPage = forwardRef<
|
||||
hasMore: false,
|
||||
};
|
||||
}
|
||||
// 允许业务定制请求参数
|
||||
// Allow business to customize request parameters
|
||||
const resp = await PluginDevelopApi.LibraryResourceList(
|
||||
entityConfigs.reduce<LibraryResourceListRequest>(
|
||||
(res, config) => config.parseParams?.(res) ?? res,
|
||||
@@ -255,7 +255,7 @@ export const BaseLibraryPage = forwardRef<
|
||||
loading: listResp.loading,
|
||||
dataSource: listResp.data?.list,
|
||||
columns,
|
||||
// 整行点击
|
||||
// Click on the whole line
|
||||
onRow: (record?: ResourceInfo) => {
|
||||
if (
|
||||
!record ||
|
||||
|
||||
@@ -25,61 +25,61 @@ import {
|
||||
|
||||
export interface LibraryEntityConfig {
|
||||
/**
|
||||
* 资源类型筛选器配置,传入级联选择器的数据类型
|
||||
* Resource type filter configuration, passing in the data type of the cascading selector
|
||||
**/
|
||||
typeFilter?: CascaderData & ({ filterName: string } | { label: string });
|
||||
|
||||
/**
|
||||
* 允许各个业务定制请求参数的格式化逻辑,避免特化逻辑侵入到底层组件中
|
||||
* @param params 原始的 query 参数
|
||||
* @returns 格式化后的 query 参数
|
||||
* Allows each business to customize the formatting logic of request parameters to avoid intrusion of specialized logic into the underlying components
|
||||
* @Param params original query parameters
|
||||
* @Returns formatted query parameters
|
||||
*/
|
||||
parseParams?: (
|
||||
params: LibraryResourceListRequest,
|
||||
) => LibraryResourceListRequest;
|
||||
|
||||
/**
|
||||
* 渲染创建菜单
|
||||
* @param params 相关参数
|
||||
* @params params.spaceId 空间 ID
|
||||
* @params params.isPersonalSpace 是否是个人空间
|
||||
* @params params.reloadList 刷新列表 API
|
||||
* @returns 渲染结果
|
||||
* Render Create Menu
|
||||
* @param params related parameters
|
||||
* @Params params.spaceId Space ID
|
||||
* @Params params.isPersonalSpace is a personal space
|
||||
* @Params params.reloadList Refresh List API
|
||||
* @Returns render result
|
||||
*/
|
||||
renderCreateMenu?: () => ReactNode;
|
||||
|
||||
// #region 表格配置
|
||||
// #region table configuration
|
||||
/**
|
||||
* 匹配数据项是否由当前配置控制渲染
|
||||
* Whether matching data items are rendered by the current configuration
|
||||
*/
|
||||
target: ResType[];
|
||||
/**
|
||||
* 表格行点击事件回调,一般用于打开详情弹窗或者跳转详情页
|
||||
* @param item 点击行数据;
|
||||
* Table row click event callback, usually used to open the details pop-up window or jump to the details page
|
||||
* @param item click row data;
|
||||
* @returns void;
|
||||
*/
|
||||
onItemClick: (item: ResourceInfo) => void;
|
||||
/**
|
||||
* 渲染表格资源信息列内容,不传则默认使用通用组件进行渲染
|
||||
* @param item 行数据
|
||||
* @returns 渲染结果
|
||||
* Render the content of the table resource information column. If it is not passed, it will be rendered using general components by default.
|
||||
* @param item row data
|
||||
* @Returns render result
|
||||
*/
|
||||
renderItem?: (item: ResourceInfo) => ReactNode;
|
||||
/**
|
||||
* 渲染资源类型文案,缺省会使用 typeFilter 中的 label
|
||||
* Render the resource type copy, using the label in the typeFilter by default
|
||||
* @param resType
|
||||
* @returns
|
||||
*/
|
||||
renderResType?: (item: ResourceInfo) => string | undefined;
|
||||
/**
|
||||
* 渲染表格操作列内容
|
||||
* @param item 行数据
|
||||
* @param reloadList 刷新列表 API
|
||||
* @returns 渲染结果
|
||||
* Render table operation column content
|
||||
* @param item row data
|
||||
* @param reloadList API
|
||||
* @Returns render result
|
||||
*/
|
||||
renderActions: (item: ResourceInfo, reloadList: () => void) => ReactNode;
|
||||
|
||||
// #endregion 表格配置
|
||||
// #endregion table configuration
|
||||
}
|
||||
|
||||
export interface ListData {
|
||||
|
||||
@@ -19,11 +19,11 @@ import { pick } from 'lodash-es';
|
||||
import { type PluginNavType } from '@coze-studio/bot-plugin-store/src/context';
|
||||
|
||||
/**
|
||||
* 比较两个对象是否相等,只比较指定的 key,通过 JSON.stringify 实现
|
||||
* @param obj1 对象1
|
||||
* @param obj2 对象2
|
||||
* @param keys 需要比较的 key
|
||||
* @returns 是否相等
|
||||
* Compares two objects for equality, comparing only the specified key, implemented by JSON.stringify
|
||||
* @param obj1
|
||||
* @param obj2
|
||||
* @Param keys The key to compare
|
||||
* @Returns is equal
|
||||
*/
|
||||
export function compareObjects<T>(
|
||||
obj1: T,
|
||||
|
||||
@@ -135,7 +135,7 @@ export const GuideModal: React.FC<GuideModalProps> = ({
|
||||
...modalProps
|
||||
}) => (
|
||||
<Modal
|
||||
// 清除 modal 自带边距 由内部 padding 撑开 展示按钮阴影
|
||||
// Clear the modal's own margins, propped up by the internal padding, and show the button shadow
|
||||
className={styles['guide-modal']}
|
||||
size="xl"
|
||||
title={I18n.t('create_title')}
|
||||
|
||||
@@ -86,7 +86,7 @@ export const ProjectFormModal: React.FC<BizProjectFormModalProps> = ({
|
||||
const auditData = await request(formApi.current.getValues());
|
||||
setAuditResult(auditData);
|
||||
|
||||
// 没有通过校验就不关闭弹窗
|
||||
// Do not close the pop-up window without passing the verification
|
||||
if (auditData.check_not_pass) {
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +125,7 @@ export const ProjectFormModal: React.FC<BizProjectFormModalProps> = ({
|
||||
{auditResult.check_not_pass ? (
|
||||
<div className="coz-fg-hglt-red mt-[-8px]">
|
||||
<ReactMarkdown skipHtml={true} linkTarget="_blank">
|
||||
{/* 注意使用 || msg undefined 或者空字符串都走兜底 */}
|
||||
{/* Note that using | | msg undefined or empty string goes to the bottom */}
|
||||
{auditResult.check_not_pass_msg || I18n.t('publish_audit_pop7')}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
|
||||
@@ -61,11 +61,11 @@ export interface ProjectTemplateBaseProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要特别处理、放到「基础」类别中的模版
|
||||
* 本身业务中没有「基础」这个类别,但是在这个通过复制创建的场景下 pm 希望为用户提供一些具有基本功能有代表性的模版
|
||||
* 所以这个特殊处理的模版应运而生
|
||||
* Templates that require special handling and are placed in the "Basic" category
|
||||
* There is no "foundation" category in the business itself, but in this scenario created by replication, pm wants to provide users with some representative templates with basic functions
|
||||
* And so this special treatment template came into being.
|
||||
*
|
||||
* 迭代时需要注意,业务上需要保证这些模版都是被 recommend 才能复用 PublicGetProductList 这个接口
|
||||
* When iterating, it is necessary to ensure that these templates are recommended in order to reuse PublicGetProductList this interface
|
||||
*/
|
||||
const BASE_TEMPLATE_ID_LIST = ['7439261984903938074'];
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import { Button, Image } from '@coze-arch/coze-design';
|
||||
import styles from './card.module.less';
|
||||
|
||||
export interface ProjectTemplateCardContentProps {
|
||||
/** 埋点参数 页面来源 */
|
||||
/** Event tracking parameters, page source */
|
||||
viewSource: ParamsTypeDefine[EVENT_NAMES.template_action_front]['source'];
|
||||
product: ProductInfo;
|
||||
onCopyTemplate?: (param: { name: string; id: string }) => void;
|
||||
|
||||
@@ -58,8 +58,8 @@ export interface CreateProjectHookProps
|
||||
onBeforeCopyProjectTemplate?: BeforeProjectTemplateCopyCallback;
|
||||
onProjectTemplateCopyError?: () => void;
|
||||
/**
|
||||
* navi 导航栏
|
||||
* space workspace 右上角的按钮
|
||||
* Navigation bar
|
||||
* Button in the upper right corner of the space workspace
|
||||
* */
|
||||
bizCreateFrom: 'navi' | 'space';
|
||||
renderAutoGenerate?: (params: RenderAutoGenerateParams) => React.ReactNode;
|
||||
@@ -100,7 +100,7 @@ export const useCreateProjectModalBase = ({
|
||||
setGuideModalVisible(false);
|
||||
|
||||
if (guideType === 'project') {
|
||||
// 海外版和开源版不支持项目模板
|
||||
// The overseas version and the open-source version do not support project templates
|
||||
if (IS_OVERSEA || IS_OPEN_SOURCE) {
|
||||
setProjectModalVisible(true);
|
||||
return;
|
||||
|
||||
@@ -51,7 +51,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
onBefore?: BeforeProjectTemplateCopyCallback;
|
||||
onError?: () => void;
|
||||
onSuccess?: ProjectTemplateCopySuccessCallback;
|
||||
/** 埋点参数 - 当前页面/来源 */
|
||||
/** Event tracking parameters - current page/source */
|
||||
source: NonNullable<
|
||||
ParamsTypeDefine[EVENT_NAMES.template_action_front]['source']
|
||||
>;
|
||||
@@ -161,7 +161,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
...rest
|
||||
}: ProjectTemplateCopyValue & {
|
||||
isSelectSpace: boolean;
|
||||
/** 用于提取埋点参数 */
|
||||
/** Used to extract event tracking parameters */
|
||||
sourceProduct: ProductInfo;
|
||||
}) => {
|
||||
setSelectSpace(inputIsSelectSpace);
|
||||
|
||||
@@ -18,7 +18,7 @@ import { createContext, type RefObject, useContext } from 'react';
|
||||
|
||||
export interface PublishContainerContextProps {
|
||||
getContainerRef: () => RefObject<HTMLDivElement> | null;
|
||||
/** 发布渠道的布局受到顶部 header 高度影响 用这个变量将他们关联起来 */
|
||||
/** The layout of the distribution channel is influenced by the height of the top header. Use this variable to associate them */
|
||||
publishHeaderHeight: number;
|
||||
setPublishHeaderHeight: (height: number) => void;
|
||||
}
|
||||
|
||||
@@ -112,18 +112,18 @@ export const UseMcpConfigModal = ({
|
||||
},
|
||||
);
|
||||
|
||||
// 只能选中未禁用的workflow
|
||||
// Only undisabled workflows can be selected.
|
||||
const filterPassList = data?.list?.filter(
|
||||
item =>
|
||||
find(item?.check_result, {
|
||||
type: CheckType.MCPPublish,
|
||||
})?.is_pass,
|
||||
);
|
||||
//半选状态
|
||||
//Half selection state
|
||||
const indeterminate =
|
||||
checkedList.length > 0 &&
|
||||
checkedList.length < (filterPassList?.length || 0);
|
||||
//全选状态
|
||||
//Select All
|
||||
const checkAll = checkedList.length === (filterPassList?.length || 0);
|
||||
|
||||
const close = () => {
|
||||
@@ -155,7 +155,7 @@ export const UseMcpConfigModal = ({
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
selectedConnectorIds: union(selectedConnectorIds, [record.id]), //ID合并去重
|
||||
selectedConnectorIds: union(selectedConnectorIds, [record.id]), //ID merge deduplicate
|
||||
});
|
||||
close();
|
||||
};
|
||||
@@ -273,14 +273,14 @@ export const UseMcpConfigModal = ({
|
||||
})}
|
||||
</Checkbox.Group>
|
||||
|
||||
{/* 加载中 */}
|
||||
{/* Loading */}
|
||||
{loadingMore && data?.list.length ? (
|
||||
<div className="text-center">
|
||||
<Spin size="small" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* 空状态 */}
|
||||
{/* empty state */}
|
||||
{!data?.list.length ? (
|
||||
<EmptyState
|
||||
className="my-[80px] mx-auto"
|
||||
|
||||
@@ -71,7 +71,7 @@ function toPublishStatus(record: PublishRecordDetail) {
|
||||
record.connector_publish_result?.some(
|
||||
item => item.connector_publish_status === ConnectorPublishStatus.Failed,
|
||||
) ?? false;
|
||||
// project 本身失败 或 部分渠道发布失败 -> 整体失败
|
||||
// The project itself failed, or some channels failed to publish - > overall failed
|
||||
if (projectFailed || connectorsFailed) {
|
||||
return PublishStatus.Failed;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function toPublishStatus(record: PublishRecordDetail) {
|
||||
item.connector_publish_status === ConnectorPublishStatus.Default ||
|
||||
item.connector_publish_status === ConnectorPublishStatus.Auditing,
|
||||
) ?? false;
|
||||
// project 本身发布中 或 部分渠道发布中 -> 整体发布中
|
||||
// The project itself is being released, or some channels are being released - > the overall release is in progress
|
||||
if (projectPublishing || connectorsPublishing) {
|
||||
return PublishStatus.Publishing;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ export function usePublishStatus({
|
||||
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
|
||||
// 轮询最新发布记录,直到不属于“发布中”状态后停止
|
||||
// Polling the latest release history until it stops when it is not in the "In Release" state
|
||||
const latestRecordRequest = useRequest(
|
||||
() => intelligenceApi.GetPublishRecordDetail({ project_id: projectId }),
|
||||
{
|
||||
@@ -122,14 +122,14 @@ export function usePublishStatus({
|
||||
pollingErrorRetryCount: 3,
|
||||
onSuccess: res => {
|
||||
const record = res.data;
|
||||
// 没有发布记录时停止轮询
|
||||
// Stop polling when no record is published
|
||||
if (!record || typeof record.publish_status !== 'number') {
|
||||
latestRecordRequest.cancel();
|
||||
return;
|
||||
}
|
||||
setStatus(toPublishStatus(record));
|
||||
setLatestRecord(record);
|
||||
// 首次请求最新发布记录后,默认选中其版本号
|
||||
// After first requesting the latest release record, its version number is selected by default
|
||||
if (!selectedVersion) {
|
||||
setRecordList([
|
||||
{ value: record.publish_record_id, label: record.version_number },
|
||||
@@ -145,7 +145,7 @@ export function usePublishStatus({
|
||||
},
|
||||
);
|
||||
|
||||
// 获取发布记录列表
|
||||
// Get a list of publication records
|
||||
const recordListRequest = useRequest(
|
||||
() => intelligenceApi.GetPublishRecordList({ project_id: projectId }),
|
||||
{
|
||||
@@ -167,7 +167,7 @@ export function usePublishStatus({
|
||||
spaceId,
|
||||
);
|
||||
|
||||
// 用户有“发布”权限时,启动轮询
|
||||
// When the user has "publish" permission, start polling
|
||||
useEffect(() => {
|
||||
if (!hasPermission || defaultRecordID) {
|
||||
return;
|
||||
@@ -175,7 +175,7 @@ export function usePublishStatus({
|
||||
latestRecordRequest.run();
|
||||
}, [hasPermission, defaultRecordID]);
|
||||
|
||||
// 手动请求选择的发布记录
|
||||
// Manually request selected release records
|
||||
const recordDetailRequest = useRequest(
|
||||
(recordId: string) =>
|
||||
intelligenceApi.GetPublishRecordDetail({
|
||||
@@ -251,7 +251,7 @@ export function usePublishStatus({
|
||||
size="mini"
|
||||
prefixIcon={tagConfig.prefixIcon}
|
||||
color={tagConfig.color}
|
||||
// Tag 组件默认 display: inline-flex, 而外层 span line-height > 1, 会导致其高度大于 Tag 本身
|
||||
// The Tag component defaults to display: inline-flex, and the outer span line-height > 1 will cause its height to be larger than the Tag itself
|
||||
className="flex !px-[3px] font-medium"
|
||||
>
|
||||
{I18n.t(tagConfig.text)}
|
||||
|
||||
@@ -91,7 +91,7 @@ export const PublishButton = ({
|
||||
intelligenceId: projectId,
|
||||
spaceId,
|
||||
enable: !!(
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
(
|
||||
FLAGS['bot.studio.publish_management'] &&
|
||||
hasPublished &&
|
||||
@@ -186,7 +186,7 @@ export const PublishButton = ({
|
||||
<Divider />
|
||||
</div>
|
||||
|
||||
{/* 即将支持,敬请期待 */}
|
||||
{/* Support soon, so stay tuned. */}
|
||||
{FLAGS['bot.studio.publish_management'] && !IS_OPEN_SOURCE ? (
|
||||
<div>
|
||||
<div className="coz-fg-secondary font-[500] px-[8px] pt-[4px] pb-0 mb-[2px]">
|
||||
|
||||
@@ -65,7 +65,7 @@ export const ApiBind = (props: ApiBindProps) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化时校验一次是否有workflow
|
||||
// Check if there is a workflow once during initialization.
|
||||
if (checked && !workflowOptionsLoading) {
|
||||
formApi.validate(['api_workflow']);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @file 开源版暂不支持商店渠道绑定,用于未来拓展
|
||||
// @File open source version does not support store channel binding for future expansion
|
||||
import { type MouseEventHandler } from 'react';
|
||||
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @file 开源版暂不支持模版渠道绑定,用于未来拓展
|
||||
|
||||
// @File open source version does not support template channel binding for future expansion
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { type MouseEventHandler, useEffect, useRef, useState } from 'react';
|
||||
|
||||
@@ -62,7 +62,7 @@ export function TemplateBind({
|
||||
|
||||
const { project_id = '' } = useParams<DynamicParams>();
|
||||
|
||||
// 回填模板配置
|
||||
// Backfill template configuration
|
||||
const fillTemplateFrom = async () => {
|
||||
const productInfo = await ProductApi.PublicGetProductEntityInfo({
|
||||
entity_id: project_id,
|
||||
@@ -78,7 +78,7 @@ export function TemplateBind({
|
||||
templateConfigured: formValues.agreement === true,
|
||||
connectors: {
|
||||
...connectors,
|
||||
// @ts-expect-error 可以接受 Partial
|
||||
// @ts-expect-error can accept Partial
|
||||
[record.id]: templateFormToBindInfo(formValues),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
@@ -86,7 +86,7 @@ export const TemplateConfigForm = forwardRef<
|
||||
// eslint-disable-next-line @coze-arch/max-line-per-function
|
||||
>(({ record, userInfo }, ref) => {
|
||||
const [FLAGS] = useFlags();
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
const customRequest = FLAGS['bot.studio.project_publish_imagex']
|
||||
? uploadCustomRequestImageX
|
||||
: uploadCustomRequest;
|
||||
@@ -96,7 +96,7 @@ export const TemplateConfigForm = forwardRef<
|
||||
const editorRef = useRef<Editor>();
|
||||
const onEditorInit = (editor: Editor) => {
|
||||
editorRef.current = editor;
|
||||
// EditorFullInput 的 form value 为纯文本,但这里需要提交 editor-kit 富文本内容
|
||||
// EditorFullInput's form value is plain text, but here you need to submit editor-kit rich text content
|
||||
editor.on(EditorEventType.CONTENT_CHANGE, _ => {
|
||||
formRef.current?.formApi?.setValue(
|
||||
'readme',
|
||||
@@ -121,7 +121,7 @@ export const TemplateConfigForm = forwardRef<
|
||||
new DeltaSet(normalizeSchema(readme as DeltaSetOptions)),
|
||||
);
|
||||
}
|
||||
// @ts-expect-error -- values 就是 TemplateForm 类型
|
||||
// @ts-expect-error -- values is the TemplateForm type
|
||||
Object.keys(values).forEach(key => formApi.setError(key, null));
|
||||
},
|
||||
validate: () => formRef.current?.formApi?.validate(),
|
||||
@@ -148,7 +148,7 @@ export const TemplateConfigForm = forwardRef<
|
||||
className="mt-[16px]"
|
||||
rules={[
|
||||
{
|
||||
// 必须勾选同意协议才能通过验证
|
||||
// The consent agreement must be checked to pass the verification.
|
||||
validator: (_rule: unknown, value: unknown) =>
|
||||
(value as boolean) === true,
|
||||
message: I18n.t('template_buy_paid_agreement_toast'),
|
||||
@@ -195,7 +195,7 @@ export const TemplateConfigForm = forwardRef<
|
||||
(value as FileItem[] | undefined)?.every(
|
||||
item => !item._sizeInvalid && item.status === 'success',
|
||||
) === true,
|
||||
message: '', // 校验文件大小是否符合限制 && 上传是否成功,Upload 组件会显示错误信息
|
||||
message: '', // Verify whether the file size meets the limit & & whether the upload was successful, the Upload component will display an error message
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface TemplatePreviewCardProps {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 基本是复制的 packages/studio/template/pages/src/components/template-list-card/index.tsx
|
||||
// Basically copied packages/studio/template/pages/src/components/template-list-card/index.tsx
|
||||
export function TemplatePreviewCard({
|
||||
userInfo,
|
||||
cover,
|
||||
|
||||
@@ -42,9 +42,9 @@ export interface TemplateForm {
|
||||
name: string;
|
||||
covers: Partial<FileItem>[];
|
||||
description: string;
|
||||
// EditorFullInput 的纯文本 form value ,仅为满足类型要求,不在业务中使用
|
||||
// EditorFullInput plain text form value, only to meet the type requirements, not used in business
|
||||
readme_text: string;
|
||||
// 实际需要传给后端的 editor-kit 富文本内容
|
||||
// The editor-kit rich text content that actually needs to be passed to the backend
|
||||
readme: string;
|
||||
preview_type: DisplayScreen;
|
||||
category: string;
|
||||
@@ -64,13 +64,13 @@ export function entityInfoToTemplateForm(
|
||||
const isZh = I18n.language.startsWith('zh');
|
||||
const meta = info.meta_info ?? {};
|
||||
const form: Partial<TemplateForm> = {
|
||||
// 默认勾选同意模板付费协议:已经上架过 或 已经配置过模板信息(readme 非空)
|
||||
// By default, check Agree to the template payment agreement: already on the shelves, or already configured template information (readme is not empty)
|
||||
agreement: meta.status !== ProductStatus.NeverListed || meta.readme !== '',
|
||||
name: meta.name,
|
||||
covers: meta.covers?.map(c => ({
|
||||
url: c.url,
|
||||
response: c,
|
||||
// 补充 FileItem 的其他属性,供表单校验使用
|
||||
// Supplements other properties of FileItem for form validation
|
||||
status: 'success',
|
||||
_sizeInvalid: false,
|
||||
})),
|
||||
|
||||
@@ -36,7 +36,7 @@ interface UnbindButtonProps {
|
||||
|
||||
const PROJECT_AGENT_TYPE = 1;
|
||||
|
||||
// 用于Api or WebSdk 的撤销发布
|
||||
// Unpublish for APIs or WebSDKs
|
||||
export const UndoButton = (props: UnbindButtonProps) => {
|
||||
const {
|
||||
bindId,
|
||||
|
||||
@@ -90,8 +90,8 @@ export function WebSdkBind({ checked, record, onClick }: WebSdkBindProps) {
|
||||
renderOptionItem={(option: optionRenderProps) => (
|
||||
<OptionWithTooltip option={option} tooltip={option.tooltip} />
|
||||
)}
|
||||
// onChange 负责处理数据清空的逻辑
|
||||
// onSelect 处理数据选择的逻辑
|
||||
// onChange is responsible for handling the logic of data emptying
|
||||
// onSelect handles data selection logic
|
||||
onChange={values => {
|
||||
if (typeof values !== 'undefined') {
|
||||
return;
|
||||
@@ -106,7 +106,7 @@ export function WebSdkBind({ checked, record, onClick }: WebSdkBindProps) {
|
||||
required: checked,
|
||||
message: I18n.t('project_release_Please_select'),
|
||||
},
|
||||
// 校验已选择的 chatflow 是否存在 && 未被禁用
|
||||
// Verify that the selected chatflow exists & & is not disabled
|
||||
{
|
||||
validator: (_rule: unknown, value: unknown) => {
|
||||
if (!checked) {
|
||||
|
||||
@@ -45,7 +45,7 @@ const TipTag: React.FC<TipTagProps> = ({ showText, tip, tagProps }) => (
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
/** 需要展示配置状态的渠道类别 */
|
||||
/** Channel categories that need to display configuration status */
|
||||
const Classes = [
|
||||
ConnectorClassification.SocialPlatform,
|
||||
ConnectorClassification.MiniProgram,
|
||||
@@ -64,7 +64,7 @@ export const ConfigStatus = ({ record }: { record: PublishConnectorInfo }) => {
|
||||
|
||||
return (
|
||||
<div className="flex gap-[6px]">
|
||||
{/* 配置状态 */}
|
||||
{/* configuration status */}
|
||||
<Tag color={color} size="mini" className="font-[500]">
|
||||
{text}
|
||||
</Tag>
|
||||
|
||||
@@ -58,7 +58,7 @@ export function ConnectorAction(props: ConnectorActionProps) {
|
||||
mouseEvent.stopPropagation();
|
||||
};
|
||||
|
||||
// 绑定/解除绑定是同一个回调,可以根据 bind_id 是否为空来区分
|
||||
// The bind/unbind is the same callback, which can be distinguished by whether the bind_id is empty or not
|
||||
const kvBindSuccessCallback = (value?: PublishConnectorInfo) => {
|
||||
if (value) {
|
||||
const isUnbind = !value.bind_id;
|
||||
@@ -100,7 +100,7 @@ export function ConnectorAction(props: ConnectorActionProps) {
|
||||
case ConnectorBindType.KvBind:
|
||||
case ConnectorBindType.KvAuthBind:
|
||||
return (
|
||||
// 使用 basis-full 强制 flex row 换行
|
||||
// Force flex row wrap with basis-full
|
||||
<div
|
||||
className={classNames(
|
||||
'basis-full self-end',
|
||||
@@ -155,7 +155,7 @@ export function ConnectorAction(props: ConnectorActionProps) {
|
||||
onClick={stopEventPropagation}
|
||||
/>
|
||||
);
|
||||
// 开源版暂不支持商店渠道绑定,用于未来拓展
|
||||
// The open-source version does not support store channel binding for the time being, for future expansion
|
||||
case ConnectorBindType.StoreBind:
|
||||
return (
|
||||
<StoreBind
|
||||
@@ -164,9 +164,9 @@ export function ConnectorAction(props: ConnectorActionProps) {
|
||||
onClick={stopEventPropagation}
|
||||
/>
|
||||
);
|
||||
// 开源版暂不支持模板渠道绑定,用于未来拓展
|
||||
// bind_type=9 用作扣子第一方渠道的标识,需要按照渠道 ID 展示绑定方式
|
||||
// TODO 后端更新 ConnectorBindType 类型定义
|
||||
// The open-source version does not support template channel binding for future expansion
|
||||
// bind_type = 9 is used as the logo of the first-party channel of the button, and the binding method needs to be displayed according to the channel ID.
|
||||
// TODO backend updates ConnectorBindType type definition
|
||||
case ConnectorBindType.TemplateBind: {
|
||||
if (record.id === TEMPLATE_CONNECTOR_ID) {
|
||||
return <TemplateBind record={record} onClick={stopEventPropagation} />;
|
||||
|
||||
@@ -45,13 +45,13 @@ import { ConfigStatus } from './config-status';
|
||||
import { UndoButton } from './bind-actions/undo-button';
|
||||
|
||||
enum DisabledReason {
|
||||
/** 社交渠道未选择 chatflow */
|
||||
/** Chatflow is not selected for social channels. */
|
||||
SocialPlatform,
|
||||
/** 未绑定 未授权 */
|
||||
/** Unbound, Unauthorized */
|
||||
NotConfigured,
|
||||
/** 后端下发的原因 */
|
||||
/** The reason for the back-end delivery */
|
||||
NotAllowed,
|
||||
/** 未配置模板 */
|
||||
/** No template configured */
|
||||
Template,
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ function getConnectorDisabledConfig({
|
||||
ConnectorClassification.SocialPlatform &&
|
||||
!socialPlatformConfig?.selected_workflows?.[0]?.workflow_id
|
||||
) {
|
||||
// 发布到社交渠道,且未选择 chatflow
|
||||
// Post to social channels without Chatflow selected.
|
||||
return {
|
||||
reason: DisabledReason.SocialPlatform,
|
||||
text: I18n.t('project_release_chatflow4'),
|
||||
@@ -88,18 +88,18 @@ function getConnectorDisabledConfig({
|
||||
reason: DisabledReason.NotConfigured,
|
||||
text: I18n.t('project_release_set_desc'),
|
||||
};
|
||||
// 未绑定 未授权
|
||||
// Unbound, Unauthorized
|
||||
if (getConnectorNotConfigured(connector)) {
|
||||
return notConfigured;
|
||||
}
|
||||
// 后端下发的不能发布的原因
|
||||
// The reason why it cannot be released after being issued by the backend.
|
||||
if (!connector.allow_publish && connector.not_allow_publish_reason) {
|
||||
return {
|
||||
reason: DisabledReason.NotAllowed,
|
||||
text: connector.not_allow_publish_reason,
|
||||
};
|
||||
}
|
||||
// 未配置模板
|
||||
// No template configured
|
||||
if (connector.id === TEMPLATE_CONNECTOR_ID && !templateConfigured) {
|
||||
return {
|
||||
reason: DisabledReason.Template,
|
||||
@@ -127,9 +127,9 @@ function getConnectorDisabledConfig({
|
||||
}
|
||||
}
|
||||
|
||||
// 与后端约定的额外描述信息
|
||||
// Additional descriptive information agreed upon with the backend
|
||||
interface DescriptionExtra {
|
||||
// 渠道名称 hover 的 tooltip
|
||||
// Channel name hover tooltip
|
||||
text?: string;
|
||||
}
|
||||
|
||||
@@ -172,15 +172,15 @@ export function ConnectorCard({
|
||||
connectorPublishConfig,
|
||||
connectorConfigMap: connectors,
|
||||
});
|
||||
// 开源版暂不支持社交平台渠道,用于未来拓展。
|
||||
// 社交渠道未选择“处理消息的对话流”时,需要将整个卡片展示为禁用态
|
||||
// The open-source version does not support social platform channels for future expansion.
|
||||
// When the social channel does not select "Process message conversation flow", the entire card needs to be displayed as disabled.
|
||||
const cardDisabled = disabledConfig?.reason === DisabledReason.SocialPlatform;
|
||||
|
||||
const descriptionExtra = (typeSafeJSONParse(
|
||||
connectorInfo.description_extra,
|
||||
) ?? {}) as DescriptionExtra;
|
||||
|
||||
// 如果禁用状态发生了变动,取消勾选当前渠道
|
||||
// If the disabled status changes, uncheck the current channel
|
||||
useEffect(() => {
|
||||
if (checked && disabledConfig) {
|
||||
onCheckedChange(false);
|
||||
@@ -321,7 +321,7 @@ export function ConnectorCard({
|
||||
<ConnectorAction
|
||||
record={connectorInfo}
|
||||
checked={checked}
|
||||
// 这个组件内部有 modal 不能使用条件渲染
|
||||
// This component has modal inside and cannot use conditional rendering.
|
||||
authActionWrapperClassName={classNames(!isShowAction && 'hidden')}
|
||||
/>
|
||||
{connectorInfo.connector_classification ===
|
||||
@@ -334,7 +334,7 @@ export function ConnectorCard({
|
||||
onClick={stopEventPropagation}
|
||||
/>
|
||||
) : null}
|
||||
{/* 开源版暂不支持MCP服务渠道,用于未来拓展 */}
|
||||
{/* The open-source version does not support MCP service channels for future expansion */}
|
||||
{connectorInfo.connector_classification ===
|
||||
ConnectorClassification.CozeSpaceExtensionLibrary &&
|
||||
connectorInfo.bind_type === ConnectorBindType.TemplateBind && (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.highlight {
|
||||
border-radius: 4px;
|
||||
animation: highlight-animation 3s forwards; /* 持续3秒的动画 */
|
||||
animation: highlight-animation 3s forwards; /* Animation lasting 3 seconds */
|
||||
}
|
||||
|
||||
@keyframes highlight-animation {
|
||||
|
||||
@@ -49,7 +49,7 @@ export function ConnectorGroupHeader({
|
||||
type,
|
||||
onAnimationEnd,
|
||||
}: ConnectorGroupHeaderProps) {
|
||||
// 付费墙
|
||||
// paywall
|
||||
const isAPIOrSDK = type === ConnectorClassification.APIOrSDK;
|
||||
const isAvailable = useBenefitAvailable({
|
||||
scene: PremiumPaywallScene.API,
|
||||
|
||||
@@ -61,7 +61,7 @@ export const ConnectorTabbar = forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
// ! 80px 高度影响 styles.mask 计算
|
||||
// ! 80Px height affects styles.mask calculation
|
||||
'flex items-center gap-x-8px h-[80px] relative',
|
||||
styles.mask,
|
||||
className,
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
import { UseMcpConfigModal } from '@/hooks/use-mcp-config-modal';
|
||||
|
||||
/** MCP配置按钮+弹窗 */
|
||||
/** MCP configuration button + pop-up window */
|
||||
export const McpConfigBtn = ({ record }: { record: PublishConnectorInfo }) => {
|
||||
const { node, open } = UseMcpConfigModal({ record });
|
||||
return (
|
||||
|
||||
@@ -83,9 +83,9 @@ export const SocialPlatformChatflow: React.FC<{ className?: string }> = ({
|
||||
handleSelectChatflow(option as ChatflowOptionProps)
|
||||
}
|
||||
rules={[
|
||||
// 有选择 SocialPlatform 时, chatflow 必填
|
||||
// Chatflow is required when SocialPlatform is selected
|
||||
{ required: hasSelectedSocialPlatforms },
|
||||
// 校验已选择的 chatflow 是否存在 && 未被禁用
|
||||
// Verify that the selected chatflow exists & & is not disabled
|
||||
{
|
||||
validator: (_rule: unknown, value: unknown) => {
|
||||
if (!hasSelectedSocialPlatforms) {
|
||||
|
||||
@@ -72,7 +72,7 @@ const getActiveConnectorTarget = ({
|
||||
|
||||
return activeConnector?.target;
|
||||
};
|
||||
/** 经验值 */
|
||||
/** experience point */
|
||||
const LOCK_TIME = 300;
|
||||
|
||||
export const useConnectorScroll = () => {
|
||||
@@ -97,14 +97,14 @@ export const useConnectorScroll = () => {
|
||||
});
|
||||
|
||||
/**
|
||||
* 需要 3 个条件同时满足
|
||||
* 1. 随着页面滚动到不同锚点,tab 栏激活对应区域的按钮
|
||||
* 2. 点击 tab 栏 对应按钮激活 直接滚动到页面对应区域
|
||||
* 3. 当页面高度不足滚动时,点击 tab 栏也要激活对应按钮
|
||||
* All three conditions need to be met simultaneously
|
||||
* 1. As the page scrolls to different anchors, the tab bar activates the button for the corresponding area
|
||||
* 2. Click the tab bar, activate the corresponding button, and scroll directly to the corresponding area of the page
|
||||
* 3. When the page height is insufficient to scroll, click the tab bar to activate the corresponding button
|
||||
*
|
||||
* 由于滚动是 smooth 效果 条件 2 与 3 会发生冲突 当用户点击 tab 栏滚动时需要进行 lock
|
||||
* lock 时 条件 1 不触发
|
||||
* 需要给条件 3 一个兜底的解锁机制
|
||||
* Since the scroll is a smooth effect, conditions 2 and 3 will conflict, and the lock needs to be performed when the user clicks the tab bar to scroll.
|
||||
* Condition 1 does not fire when locked
|
||||
* Need to give condition 3 an unlocking mechanism with a bottom line
|
||||
*/
|
||||
const manualScrollLockRef = useRef(false);
|
||||
|
||||
@@ -116,12 +116,12 @@ export const useConnectorScroll = () => {
|
||||
manualScrollLockRef.current = false;
|
||||
};
|
||||
|
||||
/** 停止滚动 LOCK_TIME 后解锁 */
|
||||
/** Unlock after LOCK_TIME */
|
||||
const manualScrollUnLockDebounce = useDebounceFn(manualScrollUnLock, {
|
||||
wait: LOCK_TIME,
|
||||
});
|
||||
|
||||
/** 兜底解锁机制 如果用户点击 tab 栏但没有解锁 LOCK_TIME 后也应该解锁 */
|
||||
/** The bottom unlocking mechanism, if the user clicks the tab bar but does not unlock the LOCK_TIME should also be unlocked */
|
||||
const baseUnLockDebounce = useDebounceFn(manualScrollUnLock, {
|
||||
wait: LOCK_TIME,
|
||||
});
|
||||
@@ -149,7 +149,7 @@ export const useConnectorScroll = () => {
|
||||
changeActiveConnectorTarget();
|
||||
|
||||
const onScroll = () => {
|
||||
// 页面发生了滚动则不需要兜底机制
|
||||
// If the page scrolls, no safety net mechanism is required
|
||||
baseUnLockDebounce.cancel();
|
||||
manualScrollUnLockDebounce.run();
|
||||
changeActiveConnectorTarget();
|
||||
|
||||
@@ -100,7 +100,7 @@ export function PublishBasicInfo() {
|
||||
'left-0',
|
||||
'!coz-bg-max',
|
||||
)}
|
||||
// 比渠道 tab 高就可以, 避免遮挡
|
||||
// It is higher than the channel tab to avoid occlusion.
|
||||
textAreaStyle={{ zIndex: CONNECTOR_TAB_BAR_Z_INDEX + 1 }}
|
||||
onChange={value => {
|
||||
setProjectPublishInfo({
|
||||
|
||||
@@ -64,7 +64,7 @@ export function PublishConnectors() {
|
||||
const { project_id = '' } = useParams<DynamicParams>();
|
||||
|
||||
const {
|
||||
connectorList, // 开源版仅支持API 和 Chat SDK 渠道
|
||||
connectorList, // The open-source version only supports API and Chat SDK channels
|
||||
connectorUnionMap,
|
||||
monetizeConfig,
|
||||
selectedConnectorIds,
|
||||
@@ -122,7 +122,7 @@ export function PublishConnectors() {
|
||||
}
|
||||
};
|
||||
|
||||
// Collapse Panel 展开状态下,不在 header 中展示已选渠道的图标
|
||||
// Collapse Panel does not display the icon of the selected channel in the header when expanded
|
||||
const getGroupHeaderList = (groupId: ConnectorClassification) => {
|
||||
const group = connectorGroups.find(g => g.type === groupId);
|
||||
if (!group) {
|
||||
@@ -189,7 +189,7 @@ export function PublishConnectors() {
|
||||
))}
|
||||
</ConnectorTabbar>
|
||||
{connectorGroups.map((i, index) => {
|
||||
// 开源版暂不支持社交平台渠道
|
||||
// The open-source version does not support social platform channels for the time being
|
||||
const isSocialPlatform =
|
||||
i.type === ConnectorClassification.SocialPlatform;
|
||||
return (
|
||||
@@ -208,7 +208,7 @@ export function PublishConnectors() {
|
||||
closeAnimation(i.type);
|
||||
}}
|
||||
/>
|
||||
{/* 开源版暂不支持社交平台渠道 */}
|
||||
{/* The open-source version does not support social platform channels for the time being */}
|
||||
{isSocialPlatform ? (
|
||||
<SocialPlatformChatflow className="mb-8px" />
|
||||
) : null}
|
||||
|
||||
@@ -44,7 +44,7 @@ export const PublishRecord: FC<{
|
||||
type: IntelligenceType.Project,
|
||||
spaceId,
|
||||
intelligenceId: projectId,
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
enable: FLAGS['bot.studio.publish_management'] && !IS_OPEN_SOURCE,
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ export const PublishRecord: FC<{
|
||||
<div className="text-[12px] coz-fg-dim leading-[16px]">
|
||||
{I18n.t('project_release_already_released_desc')}
|
||||
</div>
|
||||
{/* 即将支持,敬请期待 */}
|
||||
{/* Support soon, so stay tuned. */}
|
||||
{FLAGS['bot.studio.publish_management'] && !IS_OPEN_SOURCE ? (
|
||||
<div className="text-[12px] coz-fg-dim leading-[16px]">
|
||||
{I18n.t('release_management_detail1', {
|
||||
|
||||
@@ -77,7 +77,7 @@ export function PublishTitleBar() {
|
||||
);
|
||||
const [publishing, setPublishing] = useState(false);
|
||||
const { publishHeaderHeight, setPublishHeaderHeight } = usePublishContainer();
|
||||
// 发布结果轮询
|
||||
// publish results poll
|
||||
const { run: getPublishRecordDetail, cancel } = useRequest(
|
||||
async (params: GetPublishRecordDetailRequest) =>
|
||||
await intelligenceApi.GetPublishRecordDetail(params),
|
||||
@@ -123,7 +123,7 @@ export function PublishTitleBar() {
|
||||
selectedConnectorIds.forEach(id => {
|
||||
const connectorId = unions[id] ?? id;
|
||||
publishConnectors[connectorId] = connectors[connectorId] ?? {};
|
||||
// 社交平台的 chatflow 选项统一
|
||||
// Unified chatflow options for social platforms
|
||||
if (
|
||||
connectorList.find(c => c.id === connectorId)
|
||||
?.connector_classification ===
|
||||
@@ -154,7 +154,7 @@ export function PublishTitleBar() {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 校验的错误还需要Toast提示出来
|
||||
// Verification errors also need to be Toast Notification
|
||||
if (typeof Object.values(error as Error)[0] === 'string') {
|
||||
Toast.error(Object.values(error as Error)[0]);
|
||||
}
|
||||
|
||||
@@ -20,35 +20,35 @@ import {
|
||||
ConnectorConfigStatus,
|
||||
} from '@coze-arch/idl/intelligence_api';
|
||||
|
||||
// 未配置/授权场景
|
||||
// Unconfigured/Authorized Scenario
|
||||
export const getConnectorNotConfigured = (
|
||||
connector: PublishConnectorInfo,
|
||||
): boolean => {
|
||||
const { bind_type, config_status } = connector;
|
||||
// 未绑定&未授权
|
||||
// Unbound & Unauthorized
|
||||
const notConfigured =
|
||||
[
|
||||
ConnectorBindType.KvBind,
|
||||
ConnectorBindType.AuthBind,
|
||||
ConnectorBindType.KvAuthBind,
|
||||
ConnectorBindType.TemplateBind, // mcp未配置时禁用,模版始终为已配置
|
||||
ConnectorBindType.TemplateBind, // Disable when mcp is not configured, the template is always configured
|
||||
].includes(bind_type) &&
|
||||
config_status === ConnectorConfigStatus.NotConfigured;
|
||||
return notConfigured;
|
||||
};
|
||||
|
||||
// 不能发布的场景:
|
||||
// 1. 未绑定&未授权
|
||||
// 2. 后端下发的不能发布(如:没有workflow不能发api,有私有插件不能发模板,审核中不能发布的渠道)
|
||||
// Scenarios that cannot be published:
|
||||
// 1. Unbound & Unauthorized
|
||||
// 2. Those sent by the backend cannot be released (such as: APIs cannot be sent without workflow, templates cannot be sent with private plugins, and channels that cannot be released during review)
|
||||
export const getDisabledPublish = (
|
||||
connector: PublishConnectorInfo,
|
||||
): boolean => {
|
||||
const { allow_publish } = connector;
|
||||
// 未绑定&未授权
|
||||
// Unbound & Unauthorized
|
||||
const notConfigured = getConnectorNotConfigured(connector);
|
||||
|
||||
const connectorDisabled = notConfigured || !allow_publish;
|
||||
|
||||
// 审核中不能发布渠道的场景后端下发 allow_publish
|
||||
// The backend of the scenario where the channel cannot be released during the review is issued allow_publish
|
||||
return connectorDisabled;
|
||||
};
|
||||
|
||||
@@ -72,12 +72,12 @@ export function formatConnectorGroups(
|
||||
}
|
||||
if (c.connector_union_id) {
|
||||
const unionId = c.connector_union_id;
|
||||
// 如果当前 union_id 已经被添加到分组中,则跳过
|
||||
// If the current union_id has already been added to the group, skip
|
||||
if (group.connectors.some(i => i.connector_union_id === unionId)) {
|
||||
continue;
|
||||
}
|
||||
let connectorInfo = c;
|
||||
// 优先取 union 选中的 connector,否则取第一个
|
||||
// Give priority to the connector selected by the union, otherwise take the first one.
|
||||
const unionSelection = connectors.find(i => i.id === unions[unionId]);
|
||||
if (unionSelection) {
|
||||
connectorInfo = unionSelection;
|
||||
|
||||
@@ -56,7 +56,7 @@ const getKvBindStatus = (record: PublishConnectorInfo): ConfigStatusUI => {
|
||||
[ConnectorConfigStatus.NotConfigured]: I18n.t(
|
||||
'bot_publish_columns_status_not_configured',
|
||||
),
|
||||
// 业务不会走到下面3个case
|
||||
// Business will not go to the following 3 cases
|
||||
[ConnectorConfigStatus.Configuring]: '',
|
||||
[ConnectorConfigStatus.Disconnected]: '',
|
||||
[ConnectorConfigStatus.NeedReconfiguring]: '',
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
|
||||
export const incrementVersionNumber = (input: string) => {
|
||||
// 定义正则表达式,匹配 "数字.数字.数字" 的模式
|
||||
// Define regular expressions that match the pattern of "number. number. number"
|
||||
const regex = /(\d+)\.(\d+)\.(\d+)/g;
|
||||
|
||||
// 使用 replace 方法和回调函数对匹配的部分进行替换
|
||||
// Use the replace method and callback function to replace the matching part
|
||||
// eslint-disable-next-line max-params
|
||||
const result = input.replace(regex, (_match, p1, p2, p3) => {
|
||||
// 将最后一个数字加 1
|
||||
// Add 1 to the last number.
|
||||
const incrementedP3 = parseInt(String(p3), 10) + 1;
|
||||
// 返回新的字符串
|
||||
// Return a new string
|
||||
return `${p1}.${p2}.${incrementedP3}`;
|
||||
});
|
||||
|
||||
|
||||
@@ -60,17 +60,17 @@ export async function initPublishStore(
|
||||
const { connector_ids = [], connector_publish_config = {} } =
|
||||
last_publish_info;
|
||||
|
||||
// 初始化默认选中的渠道
|
||||
// Initialize the default selected channel
|
||||
const initSelectedConnectors: string[] = [];
|
||||
const initConnectors: Record<string, Record<string, string>> = {};
|
||||
for (const id of connector_ids) {
|
||||
const connector = connector_list.find(c => c.id === id);
|
||||
// 过滤掉不允许发布的渠道
|
||||
// Filter out channels that are not allowed to publish
|
||||
if (!connector || getDisabledPublish(connector)) {
|
||||
continue;
|
||||
}
|
||||
if (connector.connector_union_id) {
|
||||
// 对于 union 的 connector ,选中其 union id
|
||||
// For the connector of union, select its union id.
|
||||
initSelectedConnectors.push(connector.connector_union_id);
|
||||
initConnectors[connector.id] = connector.bind_info;
|
||||
} else {
|
||||
@@ -79,7 +79,7 @@ export async function initPublishStore(
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化每个 union 选中的 connector,如果上次没发布该渠道,则选中第一个
|
||||
// Initialize the connector selected by each union, and select the first one if the channel was not published last time
|
||||
const initUnions: Record<string, string> = {};
|
||||
for (const [unionId, info] of Object.entries(connector_union_info_map)) {
|
||||
initUnions[unionId] =
|
||||
@@ -87,9 +87,9 @@ export async function initPublishStore(
|
||||
?.connector_id ?? info.connector_options[0].connector_id;
|
||||
}
|
||||
|
||||
// 回填社交渠道选择的 chatflow,优先级:
|
||||
// 1. draft 中保存的 chatflow
|
||||
// 2. 上次发布的第一个 SocialPlatform 选择的 chatflow
|
||||
// Backfill the chatflow of social channel selection, priority:
|
||||
// 1. Saved chatflows in drafts
|
||||
// 2. Chatflow selected by the first SocialPlatform released last time
|
||||
let lastSocialPlatformChatflow: ConnectorPublishConfig | undefined;
|
||||
if (draft?.socialPlatformConfig?.selected_workflows?.[0].workflow_id) {
|
||||
lastSocialPlatformChatflow = draft.socialPlatformConfig;
|
||||
@@ -109,7 +109,7 @@ export async function initPublishStore(
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 draft 中保存的信息回填 WebSDK 渠道选择的 chatflow
|
||||
// Backfill the chatflow selected by the WebSDK channel based on the information saved in the draft
|
||||
if (draft?.sdkConfig?.selected_workflows?.[0].workflow_id) {
|
||||
connector_publish_config[WEB_SDK_CONNECTOR_ID] = draft.sdkConfig;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export async function initPublishStore(
|
||||
);
|
||||
|
||||
const lastPublishVersionNumber = last_publish_info.version_number;
|
||||
// 用户没有 draft 并且存在发布过的版本 则将上一次发布的版本号进行处理
|
||||
// If the user does not have a draft and there is a published version, the last released version number will be processed
|
||||
const fixedVersionNumber = getFixedVersionNumber({
|
||||
lastPublishVersionNumber,
|
||||
draftVersionNumber: draft?.versionNumber,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { type StoreBindKey } from '@/store';
|
||||
|
||||
type SelfMapping<T extends string> = {
|
||||
[K in T]: K; // 关键语法:将每个字面量类型映射为自己
|
||||
[K in T]: K; // Key syntax: Mapping each literal type to itself
|
||||
};
|
||||
|
||||
type KeyMapping = SelfMapping<StoreBindKey>;
|
||||
@@ -25,7 +25,7 @@ type KeyMapping = SelfMapping<StoreBindKey>;
|
||||
export const isStoreBindConfigured = (
|
||||
config: Record<string, string>,
|
||||
): boolean => {
|
||||
// 防止 StoreBindKey 有变动导致 bug
|
||||
// Prevent StoreBindKey changes from causing bugs
|
||||
const { category_id, display_screen }: KeyMapping = {
|
||||
category_id: 'category_id',
|
||||
display_screen: 'display_screen',
|
||||
|
||||
@@ -69,7 +69,7 @@ const ConnectorTagMap: Record<
|
||||
color: 'green',
|
||||
text: 'project_release_success',
|
||||
},
|
||||
[ConnectorPublishStatus.Disable]: null, // 对应状态不会返回给前端,不进行适配
|
||||
[ConnectorPublishStatus.Disable]: null, // The corresponding state will not be returned to the front end and will not be adapted.
|
||||
};
|
||||
|
||||
export function ConnectorStatus({
|
||||
@@ -78,7 +78,7 @@ export function ConnectorStatus({
|
||||
onShowWebSdkGuide,
|
||||
}: ConnectorStatusProps) {
|
||||
const tagConfig = ConnectorTagMap[result.connector_publish_status ?? 0];
|
||||
// Web SDK 渠道发布成功时,展示安装指引
|
||||
// When the Web SDK channel is successfully released, the installation instructions will be displayed
|
||||
const shouldShowWebSdkGuide =
|
||||
result.connector_id === WEB_SDK_CONNECTOR_ID &&
|
||||
result.connector_publish_status === ConnectorPublishStatus.Success;
|
||||
@@ -132,13 +132,13 @@ export function ConnectorStatus({
|
||||
{result.connector_publish_status_msg}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
{/* Web SDK 渠道 - 安装指引 */}
|
||||
{/* Web SDK Channel - Installation Guide */}
|
||||
{shouldShowWebSdkGuide ? (
|
||||
<Typography.Text fontSize="14px" link onClick={showWebSdkGuide}>
|
||||
{I18n.t('project_release_guide')}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
{/* 小程序渠道 - 下载代码 & 安装指引 */}
|
||||
{/* Mini Program Channel - Download Code & Installation Guide */}
|
||||
{result.download_link ? (
|
||||
<>
|
||||
<Typography.Text
|
||||
|
||||
@@ -138,9 +138,9 @@ function PackStep(props: { record: PublishRecordDetail }) {
|
||||
const stepProps = toPackStepProps(props.record, ref, maxTagCount);
|
||||
|
||||
/**
|
||||
* TagGroup 仅支持设置 maxTagCount 控制展示 Tag 的数量,不支持设置展示行数。
|
||||
* 这里通过遍历所有 Tag 的 offsetTop 来判断其所在的行数,并将 maxTagCount
|
||||
* 设置为刚好能展示两行的数量。
|
||||
* TagGroup only supports setting maxTagCount to control the number of display tags, not the number of display rows.
|
||||
* Here, the number of rows it is in is determined by iterating over the offsetTop of all Tags, and maxTagCount
|
||||
* Set to just enough to display the number of two rows.
|
||||
*/
|
||||
useLayoutEffect(() => {
|
||||
if (!ref.current) {
|
||||
@@ -159,7 +159,7 @@ function PackStep(props: { record: PublishRecordDetail }) {
|
||||
if (top !== tagTop) {
|
||||
top = tagTop;
|
||||
rowCount++;
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- offsetTop 第三次变化,当前 Tag 处于第三行
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- offsetTop The third change, the current Tag is in the third line
|
||||
if (rowCount >= 3) {
|
||||
setMaxTagCount(i);
|
||||
break;
|
||||
@@ -234,7 +234,7 @@ function getConnectorsPublishStatus(
|
||||
item => item.connector_publish_status === ConnectorPublishStatus.Failed,
|
||||
).length;
|
||||
if (failedCount > 0) {
|
||||
// 所有渠道均失败,显示红色叉号;部分渠道失败,显示黄色感叹号
|
||||
// All channels failed with a red cross; some channels failed with a yellow exclamation mark
|
||||
return failedCount === connectorResults.length ? 'error' : 'warn';
|
||||
}
|
||||
const publishingCount = connectorResults.filter(
|
||||
@@ -243,7 +243,7 @@ function getConnectorsPublishStatus(
|
||||
item.connector_publish_status === ConnectorPublishStatus.Auditing,
|
||||
).length;
|
||||
if (publishingCount > 0) {
|
||||
// 部分渠道在发布中,显示时钟图标
|
||||
// Some channels are in the release, showing the clock icon.
|
||||
return 'process';
|
||||
}
|
||||
return 'finish';
|
||||
@@ -257,7 +257,7 @@ function toPublishStepProps(
|
||||
if (typeof record.publish_status !== 'number') {
|
||||
return getDefaultStepProps(title);
|
||||
}
|
||||
// 未到达“渠道审核与发布”步骤,显示默认的灰色时钟图标
|
||||
// The "Channel Review and Publish" step has not been reached, the default gray clock icon is displayed
|
||||
if (record.publish_status < PublishRecordStatus.ConnectorPublishing) {
|
||||
return {
|
||||
...getDefaultStepProps(title),
|
||||
@@ -272,7 +272,7 @@ function toPublishStepProps(
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
{...(IS_OVERSEA &&
|
||||
// publish_monetization_result 为 nil 表示可能是接口失败,也不展示
|
||||
// publish_monetization_result nil indicates that the interface may fail and does not show
|
||||
record.publish_monetization_result === false && {
|
||||
tag: I18n.t('monetization_publish_fail'),
|
||||
color: 'red',
|
||||
|
||||
@@ -38,7 +38,7 @@ class PublishAnchorService {
|
||||
const stringifyLocalData = localStorage.getItem(this.PUBLISH_ANCHOR_KEY);
|
||||
const localData = typeSafeJSONParse(stringifyLocalData);
|
||||
const validData = publishAnchorSchema.parse(localData);
|
||||
// 使用 zod 对 localData 进行类型校验
|
||||
// Using zod to type-check localData
|
||||
this.anchorValues = validData;
|
||||
} catch {
|
||||
this.anchorValues = {};
|
||||
|
||||
@@ -33,39 +33,39 @@ import { type ProjectPublishDraft } from './publish-main/utils/publish-draft';
|
||||
export type StoreBindKey = 'display_screen' | 'category_id';
|
||||
|
||||
export interface ProjectPublishStore {
|
||||
/** 页面加载状态 */
|
||||
/** page load status */
|
||||
pageLoading: boolean;
|
||||
/** 渠道列表 */
|
||||
/** Channel list */
|
||||
connectorList: PublishConnectorInfo[];
|
||||
/** 需要聚合的渠道列表,key 是 PublishConnectorInfo['connector_union_id'] */
|
||||
/** List of channels that need to be aggregated, the key is PublishConnectorInfo ['connector_union_id'] */
|
||||
connectorUnionMap: Record<string, ConnectorUnionInfo>;
|
||||
/** 渠道选择的id */
|
||||
/** ID of channel selection */
|
||||
selectedConnectorIds: string[];
|
||||
/** 是否展示发布结果 */
|
||||
/** Whether to display the published results */
|
||||
showPublishResult: boolean;
|
||||
/** 上次发布的版本号 */
|
||||
/** Last released version number */
|
||||
lastVersionNumber: string;
|
||||
/** 版本号 */
|
||||
/** version number */
|
||||
versionNumber: string;
|
||||
/** 版本描述 */
|
||||
/** version description */
|
||||
versionDescription: string;
|
||||
/** 渠道选择的Workflow/ChatFlow */
|
||||
/** Channel Selection Workflow/ChatFlow */
|
||||
connectorPublishConfig: Record<string, ConnectorPublishConfig>;
|
||||
/** 社交平台渠道统一选择的 chatflow */
|
||||
/** Chatflow for unified selection of social platform channels */
|
||||
socialPlatformChatflow: ConnectorPublishConfig;
|
||||
/** 发布配置信息,key代表connector_id,value是渠道发布的参数 */
|
||||
/** Release configuration information, key represents connector_id, value is the parameter of channel release */
|
||||
connectors: Record<string, Record<string, string>>;
|
||||
/** 聚合渠道的选择信息,key代表connector_union_id,value是union的选择信息 */
|
||||
/** Aggregate channel selection information, key represents connector_union_id, value is union selection information */
|
||||
unions: Record<string, string>;
|
||||
/** 是否已经配置过模板信息 */
|
||||
/** Whether the template information has been configured */
|
||||
templateConfigured: boolean;
|
||||
/** 发布结果详情(轮询接口返回值) */
|
||||
/** Publish result details (poll interface return value) */
|
||||
publishRecordDetail: PublishRecordDetail &
|
||||
// 该信息由 PublishProject 接口返回
|
||||
// 但为了符合现有数据流转逻辑(PublishProject 拿到 id,用 id 轮询 GetPublishRecordDetail 拿到结果作为唯一数据源)
|
||||
// 因此前端手动将该值拼到轮询结果中
|
||||
// This information is returned by the PublishProject interface
|
||||
// But in order to conform to the existing data flow logic (PublishProject gets the id, polls with the id GetPublishRecordDetail gets the result as the only data source)
|
||||
// So the frontend manually spells the value into the polling results
|
||||
Pick<PublishProjectData, 'publish_monetization_result'>;
|
||||
/** 付费配置 */
|
||||
/** paid configuration */
|
||||
monetizeConfig?: BotMonetizationConfigData;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const initialStore: ProjectPublishStore = {
|
||||
versionDescription: '',
|
||||
connectorPublishConfig: {},
|
||||
socialPlatformChatflow: {},
|
||||
// 默认已经配置过模板信息(不影响模板渠道默认勾选),当 template-bind 组件初始化获取到模板信息后,再按需设置为 false
|
||||
// The template information has been configured by default (it does not affect the template channel is checked by default). When the template-bind component initializes to obtain the template information, it is set to false on demand.
|
||||
templateConfigured: true,
|
||||
connectors: {},
|
||||
unions: {},
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** 发布为 Web SDK 的渠道 ID */
|
||||
/** Channel IDs published as Web SDKs */
|
||||
export const WEB_SDK_CONNECTOR_ID = '999';
|
||||
/** 发布到扣子 - 模板渠道的 ID */
|
||||
/** Post to Button - Template Channel ID */
|
||||
export const TEMPLATE_CONNECTOR_ID = '10000129';
|
||||
/** 发布为小程序 - 抖音小程序(自行下载)的渠道 ID */
|
||||
/** Channel ID published as Mini Program - Douyin Mini Program (self-download) */
|
||||
export const MINI_PROGRAM_DOUYIN_DOWNLOAD_CONNECTOR_ID = '10000130';
|
||||
/** 发布为小程序 - 微信小程序(自行下载)的渠道 ID */
|
||||
/** Channel ID published as Mini Program - WeChat Mini Program (self-download) */
|
||||
export const MINI_PROGRAM_WECHAT_DOWNLOAD_CONNECTOR_ID = '10000131';
|
||||
/** banner 存在时发布渠道 header 高度 */
|
||||
/** Publish channel header height when banner exists */
|
||||
export const DEFAULT_PUBLISH_HEADER_HEIGHT = 97;
|
||||
/** banner 关闭时发布渠道 header 高度 */
|
||||
/** Publish channel header height when banner is closed */
|
||||
export const MIN_PUBLISH_HEADER_HEIGHT = 65;
|
||||
/** 渠道 tab 的 z-index */
|
||||
/** z-index of channel tab */
|
||||
export const CONNECTOR_TAB_BAR_Z_INDEX = 1;
|
||||
/** 用户新发布时默认填写的 version_number */
|
||||
/** The default version_number that users fill in when they publish a new post */
|
||||
export const DEFAULT_VERSION_NUMBER = 'v0.0.1';
|
||||
|
||||
@@ -21,14 +21,14 @@ import {
|
||||
} from '@coze-arch/idl/intelligence_api';
|
||||
|
||||
/**
|
||||
* 判断发布过程是否已经结束,可以停止轮询
|
||||
* Determine whether the publishing process has ended and stop polling
|
||||
*/
|
||||
export function isPublishFinish(record: PublishRecordDetail) {
|
||||
// project 打包失败/审核不通过
|
||||
// Project packaging failed/review failed
|
||||
const projectFinish =
|
||||
record.publish_status === PublishRecordStatus.PackFailed ||
|
||||
record.publish_status === PublishRecordStatus.AuditNotPass;
|
||||
// 所有渠道均处于 审核中/失败/成功 状态
|
||||
// All channels are under review, failed, or successful
|
||||
const connectorsFinish =
|
||||
record.connector_publish_result?.every(
|
||||
item =>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { type create } from 'zustand';
|
||||
|
||||
export interface SetterAction<T> {
|
||||
/**
|
||||
* 增量更新
|
||||
* incremental update
|
||||
*
|
||||
* @example
|
||||
* // store.x: { a: 1, b: 2 }
|
||||
@@ -27,7 +27,7 @@ export interface SetterAction<T> {
|
||||
*/
|
||||
(state: Partial<T>): void;
|
||||
/**
|
||||
* 全量更新
|
||||
* full update
|
||||
*
|
||||
* @example
|
||||
* // store.x: { a: 1, b: 2 }
|
||||
|
||||
@@ -91,7 +91,7 @@ export function useWebSdkGuideModal() {
|
||||
footer={
|
||||
<Button onClick={close}>{I18n.t('app_publish_sdk_confirm')}</Button>
|
||||
}
|
||||
// z-index 需要大于 publish-status 的 Popover
|
||||
// z-index requires a Popover greater than publish-status.
|
||||
zIndex={2000}
|
||||
>
|
||||
<Typography.Paragraph className="font-medium mb-[8px]">
|
||||
|
||||
Reference in New Issue
Block a user