chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -94,7 +94,7 @@ describe('AutosaveManager', () => {
|
||||
manager.start();
|
||||
expect(manager.observerList.length).toBe(1);
|
||||
|
||||
manager.start(); // 再次调用 start
|
||||
manager.start(); // Call start again.
|
||||
expect(manager.observerList.length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -188,7 +188,7 @@ describe('AutosaveManager', () => {
|
||||
});
|
||||
|
||||
const observer = manager.getObserver('testKey');
|
||||
// 确保所有异步操作完成
|
||||
// Make sure all asynchronous operations are completed
|
||||
await Promise.resolve();
|
||||
|
||||
expect(observer.lock).toBe(false);
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('AutosaveObserver', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers(); // 使用假定时器
|
||||
vi.useFakeTimers(); // Use a hypothetical timer
|
||||
|
||||
observer = new AutosaveObserver({
|
||||
store: mockStore,
|
||||
@@ -74,7 +74,7 @@ describe('AutosaveObserver', () => {
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.useRealTimers(); // 恢复真实的定时器
|
||||
vi.useRealTimers(); // Restore the real timer
|
||||
});
|
||||
|
||||
it('should initialize and set initial values correctly', () => {
|
||||
@@ -119,9 +119,9 @@ describe('AutosaveObserver', () => {
|
||||
|
||||
expect(observer.debouncedSaveFunc).toBeInstanceOf(Function);
|
||||
|
||||
vi.runAllTimers(); // 手动推进定时器时间以触发防抖函数
|
||||
vi.runAllTimers(); // Manually advance the timer time to trigger the stabilization function
|
||||
|
||||
await vi.runAllTimersAsync(); // 确保所有异步操作完成
|
||||
await vi.runAllTimersAsync(); // Make sure all asynchronous operations are completed
|
||||
|
||||
expect(saveRequest).toHaveBeenCalledWith(nextState, 'testKey', diffChange);
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册数据源和定义对应的 Observer 配置
|
||||
* Register the data source and define the corresponding Observer configuration
|
||||
* @param _config
|
||||
*/
|
||||
public register = (
|
||||
@@ -80,7 +80,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 启动 Manager 模块
|
||||
* Launch Manager Module
|
||||
*/
|
||||
public start = () => {
|
||||
if (this.observerList.length > 0) {
|
||||
@@ -96,7 +96,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭 Manager 模块下的所有属性监听
|
||||
* Turn off all property listeners under the Manager module
|
||||
*/
|
||||
public close = () => {
|
||||
this.observerList.forEach(observer => observer.close());
|
||||
@@ -104,7 +104,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 手动保存
|
||||
* save manually
|
||||
* @param params
|
||||
*/
|
||||
public manualSave = async (key: ScopeKey, params: ScopeStateType) => {
|
||||
@@ -127,7 +127,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 回调过程中关闭自动保存
|
||||
* Turn off autosave during a callback
|
||||
* @param params
|
||||
*/
|
||||
public handleWithoutAutosave = async (params: {
|
||||
@@ -145,7 +145,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 立即触发保存
|
||||
* Trigger save immediately
|
||||
* @param key
|
||||
*/
|
||||
public saveFlush = (key: ScopeKey) => {
|
||||
@@ -154,7 +154,7 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 立即触发所有保存
|
||||
* Trigger all saves immediately
|
||||
* @param key
|
||||
*/
|
||||
public saveFlushAll = () => {
|
||||
@@ -164,14 +164,14 @@ export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取目标 observer 配置
|
||||
* Get target observer configuration
|
||||
* @param key
|
||||
*/
|
||||
private getObserver = (key: ScopeKey) =>
|
||||
this.observerList.find(i => i.config.key === key);
|
||||
|
||||
/**
|
||||
* 获取目标配置项
|
||||
* Get target configuration item
|
||||
* @param key
|
||||
*/
|
||||
private getConfig = (key: ScopeKey) =>
|
||||
|
||||
@@ -49,7 +49,7 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
this.lock = false;
|
||||
this.config = config;
|
||||
|
||||
// 订阅字段初始化
|
||||
// Subscription field initialization
|
||||
this.initSubscribe();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
if (typeof this.config.selector === 'function') {
|
||||
return this.config.selector;
|
||||
} else {
|
||||
// 使用createSelector创建可记忆化的选择器
|
||||
// Create a memorable selector with createSelector
|
||||
const { deps, transformer } = this.config.selector;
|
||||
return createSelector(deps, transformer);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
console.log('nextState :>> ', nextState);
|
||||
console.log('prevState :>> ', prevState);
|
||||
|
||||
// selector 返回的 state
|
||||
// The state returned by the selector
|
||||
this.nextState = nextState;
|
||||
this.prevState = prevState;
|
||||
|
||||
@@ -110,12 +110,12 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
private parsedSaveFunc = async () => {
|
||||
// 中间件-保存前
|
||||
// Middleware - Before saving
|
||||
const beforeSavePayload = await getPayloadByFormatter<ScopeStateType>(
|
||||
this.nextState,
|
||||
this.config?.middleware?.onBeforeSave,
|
||||
);
|
||||
// 生命周期-保存前
|
||||
// Life cycle - before saving
|
||||
await this.config?.eventCallBacks?.onBeforeSave?.({
|
||||
key: this.config.key,
|
||||
data: beforeSavePayload,
|
||||
@@ -129,21 +129,21 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
this.diff,
|
||||
);
|
||||
|
||||
// 中间件-保存后
|
||||
// Middleware - after saving
|
||||
const afterSavePayload = await getPayloadByFormatter<ScopeStateType>(
|
||||
this.nextState,
|
||||
this.config?.middleware?.onAfterSave,
|
||||
);
|
||||
console.log('afterSavePayload:>>', afterSavePayload);
|
||||
|
||||
// 生命周期-保存后
|
||||
// Life cycle - after saving
|
||||
await this.config?.eventCallBacks?.onAfterSave?.({
|
||||
key: this.config.key,
|
||||
data: afterSavePayload,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('error:>>', error);
|
||||
// 生命周期-异常
|
||||
// Life Cycle - Abnormal
|
||||
this.config?.eventCallBacks?.onError?.({
|
||||
key: this.config.key,
|
||||
error: error as Error,
|
||||
@@ -152,7 +152,7 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
* unsubscribe
|
||||
*/
|
||||
public close = () => {
|
||||
this.debouncedSaveFunc?.flush();
|
||||
@@ -161,10 +161,10 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取状态变更带来的触发延时时间
|
||||
* @param prevState selector 选择的 store 的内容
|
||||
* @param diffChange 前后比对的diff
|
||||
* @returns 延时时间
|
||||
* Get the trigger delay time caused by the state change
|
||||
* @param prevState selector to store content
|
||||
* @param diffChange the diff before and after comparison
|
||||
* @returns delay time
|
||||
*/
|
||||
private getTriggerDelayTime = (
|
||||
prevState?: ScopeStateType,
|
||||
@@ -232,7 +232,7 @@ export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取变更与 trigger 声明配置对应的 key
|
||||
* Gets the key that changes the configuration corresponding to the trigger declaration
|
||||
* @param changePath diff path
|
||||
* @returns path key
|
||||
*/
|
||||
|
||||
@@ -20,23 +20,23 @@ import { type StoreApi, type UseBoundStore } from 'zustand';
|
||||
import type { Diff } from 'deep-diff';
|
||||
|
||||
/**
|
||||
* 防抖延迟时间
|
||||
* stabilization delay time
|
||||
* @readonly
|
||||
* @enum {number}
|
||||
*/
|
||||
export enum DebounceTime {
|
||||
/** 用于需要立即响应的保存操作,如按钮或下拉选择等操作 */
|
||||
/** For saving operations that require an immediate response, such as buttons or drop-down selections */
|
||||
Immediate = 0,
|
||||
/** 用于需要较短时间响应的保存操作,如拖拽等操作 */
|
||||
/** For saving operations that require a short response time, such as dragging and dropping */
|
||||
Medium = 500,
|
||||
/** 适合于文本输入等操作 */
|
||||
/** Suitable for operations such as text input */
|
||||
Long = 1000,
|
||||
}
|
||||
|
||||
/** trigger 配置声明时的函数形式,用于在运行时指定字段触发时机 */
|
||||
/** Functional form when the trigger configuration is declared, used to specify when the field will trigger at runtime */
|
||||
export type FunctionDebounceTime = () => DebounceTime;
|
||||
|
||||
/** trigger 配置声明时的数组形式,用于分别指定数组内容变化时的触发时机 */
|
||||
/** The array form when the trigger configuration is declared, which is used to specify the trigger timing when the array content changes */
|
||||
export interface ArrayDebounceTime {
|
||||
arrayType: boolean;
|
||||
action:
|
||||
@@ -48,7 +48,7 @@ export interface ArrayDebounceTime {
|
||||
};
|
||||
}
|
||||
|
||||
/** trigger 配置声明时的对象形式,用于分别指定多字段触发时机 */
|
||||
/** The object form when the trigger configuration declaration is used to specify the triggering time of multiple fields separately */
|
||||
export interface ObjectDebounceTime {
|
||||
default: DebounceTime;
|
||||
[index: string]: DebounceTime | ArrayDebounceTime;
|
||||
@@ -79,21 +79,21 @@ export type SelectorType<StoreType, ScopeStateType> =
|
||||
};
|
||||
|
||||
export interface AutosaveObserverConfig<StoreType, ScopeKey, ScopeStateType> {
|
||||
/** 被托管的数据字段的类型 */
|
||||
/** The type of data field being hosted */
|
||||
key: ScopeKey;
|
||||
/** 防抖延迟时间 */
|
||||
/** stabilization delay time */
|
||||
debounce?: DebounceConfig;
|
||||
/** store 需要被监听的属性选择器,支持配置依赖 */
|
||||
/** Store property selectors that need to be listened to, support configuration dependencies */
|
||||
selector: SelectorType<StoreType, ScopeStateType>;
|
||||
/** 中间件 支持业务链式处理监听数据 */
|
||||
/** Middleware, which supports business chain processing of monitoring data */
|
||||
middleware?: MiddlewareHanderMap<ScopeStateType>;
|
||||
/** 是否立即保存当前字段 */
|
||||
/** Whether to save the current field immediately */
|
||||
immediate?: boolean;
|
||||
/** 保存的请求 */
|
||||
/** saved request */
|
||||
saveRequest: SaveRequest<ScopeStateType, ScopeKey>;
|
||||
/** 被托管的数据取消订阅时进行的回调 */
|
||||
/** Callbacks when hosted data is unsubscribed */
|
||||
unobserver?: () => void;
|
||||
/** 生命周期 */
|
||||
/** Life Cycle */
|
||||
eventCallBacks?: EventCallBacks<ScopeStateType, ScopeKey>;
|
||||
}
|
||||
|
||||
@@ -108,28 +108,28 @@ export type SaveMiddlewareHander<ScopeStateType> = (
|
||||
) => Promise<FlexibleState<ScopeStateType>> | FlexibleState<ScopeStateType>;
|
||||
|
||||
export interface MiddlewareHanderMap<ScopeStateType> {
|
||||
/** 生命周期-检测变更后 */
|
||||
/** Lifecycle - After detecting changes */
|
||||
onBeforeSave?: SaveMiddlewareHander<ScopeStateType>;
|
||||
/** 生命周期-成功保存后 */
|
||||
/** Lifecycle - after successful saving */
|
||||
onAfterSave?: SaveMiddlewareHander<ScopeStateType>;
|
||||
}
|
||||
|
||||
export interface EventCallBacks<ScopeStateType, ScopeKey> {
|
||||
/** 生命周期-检测变更后 */
|
||||
/** Lifecycle - After detecting changes */
|
||||
onBeforeSave?: (params: {
|
||||
data: FlexibleState<ScopeStateType>;
|
||||
key: ScopeKey;
|
||||
}) => void | Promise<void>;
|
||||
/** 生命周期-成功保存后 */
|
||||
/** Lifecycle - after successful saving */
|
||||
onAfterSave?: (params: {
|
||||
data: FlexibleState<ScopeStateType>;
|
||||
key: ScopeKey;
|
||||
}) => void | Promise<void>;
|
||||
/** 生命周期-异常 */
|
||||
/** Life Cycle - Abnormal */
|
||||
onError?: (params: { error: Error; key: ScopeKey }) => void;
|
||||
}
|
||||
|
||||
// 比对出来的被变化的 key 的 path, number 形式对应数组
|
||||
// The path of the changed key compared, corresponding to the array in the form of number
|
||||
export type PathType = string | number;
|
||||
|
||||
export interface UseStoreType<StoreType, ScopeStateType> {
|
||||
|
||||
@@ -37,7 +37,7 @@ export function isObject(value: DebounceConfig): value is ObjectDebounceTime {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存接口调用时候需要的参数
|
||||
* Get the parameters required to save the interface call
|
||||
*/
|
||||
export const getPayloadByFormatter = async <T>(
|
||||
state: T,
|
||||
|
||||
@@ -33,7 +33,7 @@ vi.mock('@coze-arch/logger', () => ({
|
||||
getSlardarInstance: vi.fn(() => mockSlardarInstance),
|
||||
}));
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('copy-to-clipboard', () => ({
|
||||
default: vi.fn(),
|
||||
}));
|
||||
@@ -105,19 +105,19 @@ describe('withSlardarIdButton', () => {
|
||||
const button = screen.getByTestId('button');
|
||||
fireEvent.click(button);
|
||||
|
||||
// 验证 slardar.config 被调用
|
||||
// Verify that slardar.config is called
|
||||
expect(getSlardarInstance).toHaveBeenCalled();
|
||||
expect(mockSlardarInstance.config).toHaveBeenCalled();
|
||||
|
||||
// 验证 copy 被调用,且参数正确
|
||||
// Verify that copy is called and that the parameters are correct
|
||||
expect(copy).toHaveBeenCalledWith('test-session-id');
|
||||
|
||||
// 验证 Toast.success 被调用,且参数正确
|
||||
// Verify that Toast.success is called and the parameters are correct
|
||||
expect(Toast.success).toHaveBeenCalledWith('复制成功');
|
||||
});
|
||||
|
||||
it('当 sessionId 为空时应该复制空字符串', () => {
|
||||
// 模拟 sessionId 为 undefined
|
||||
// Emulate sessionId to undefined
|
||||
vi.mocked(mockSlardarInstance.config).mockReturnValueOnce({
|
||||
sessionId: undefined,
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import { getFileInfo } from '../src/util';
|
||||
import { FileTypeEnum } from '../src/const';
|
||||
|
||||
// 创建模拟的 File 对象
|
||||
// Create a simulated File object
|
||||
function createMockFile(name: string, type: string): File {
|
||||
return {
|
||||
name,
|
||||
@@ -140,7 +140,7 @@ describe('getFileInfo', () => {
|
||||
});
|
||||
|
||||
it('当文件类型和扩展名不匹配时,应该优先使用文件类型判断', () => {
|
||||
// 文件名是 .txt 但 MIME 类型是图片
|
||||
// The file name is .txt, but the MIME type is image
|
||||
const file = createMockFile('image.txt', 'image/jpeg');
|
||||
const fileInfo = getFileInfo(file);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { FileTypeEnum, type TFileTypeConfig } from './const';
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
* file type
|
||||
* {@link
|
||||
* {@link https://www.iana.org/assignments/media-types/media-types.xhtml#image}
|
||||
*/
|
||||
@@ -37,7 +37,7 @@ export const FILE_TYPE_CONFIG: readonly TFileTypeConfig[] = [
|
||||
'.ogg',
|
||||
'.wma',
|
||||
'.alac',
|
||||
// .midi 和 .mid 都是MIDI(Musical Instrument Digital Interface)文件的扩展名 - GPT
|
||||
// Both .midi and .mid are extensions for MIDI (Musical Instrument Digital Interface) files - GPT
|
||||
'.mid',
|
||||
'.midi',
|
||||
'.ac3',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { FILE_TYPE_CONFIG } from './file-type';
|
||||
|
||||
// 获取文件信息
|
||||
// Get file information
|
||||
export const getFileInfo = (file: File) => {
|
||||
const fileInfo = FILE_TYPE_CONFIG.find(({ judge, accept }) =>
|
||||
judge ? judge(file) : accept.some(ext => file.name.endsWith(ext)),
|
||||
|
||||
@@ -77,10 +77,10 @@ describe('SelectSpaceModal', () => {
|
||||
wrapper.getByDisplayValue('mockBot(duplicate_rename_copy)'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// 检查表单是否存在
|
||||
// Check if the form exists
|
||||
expect(wrapper.getByRole('form')).toBeInTheDocument();
|
||||
|
||||
// 检查确定和取消按钮
|
||||
// Check OK and Cancel buttons
|
||||
expect(
|
||||
wrapper.getByRole('button', { name: 'confirm' }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
@@ -28,23 +28,23 @@ import { CarouselItem } from './carousel-item';
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface CarouselProps {
|
||||
/** 元素布局行数默认为1 */
|
||||
/** The number of rows in the element layout defaults to 1. */
|
||||
rows?: number;
|
||||
/** 元素布局列数,默认为均分数组 */
|
||||
/** Number of element layout columns, the default is to divide the array equally */
|
||||
column?: number;
|
||||
/** 每次点击箭头滚动的百分比,0~1. 默认值为0.5 */
|
||||
/** Percentage of scrolling per click of arrow, 0~ 1. Default value is 0.5 */
|
||||
scrollStep?: number;
|
||||
/** 滚动回调 */
|
||||
/** scrolling callback */
|
||||
onScroll?: () => void;
|
||||
/** 箭头是否显示边框 */
|
||||
/** Whether the arrow shows the border */
|
||||
enableArrowBorder?: boolean;
|
||||
/** 箭头是否显示阴影渐变 */
|
||||
/** Whether the arrows show gradual changes in shadows */
|
||||
enableArrowShalldow?: boolean;
|
||||
/** 子元素样式 */
|
||||
/** child style */
|
||||
itemClassName?: string;
|
||||
/** 左箭头样式 */
|
||||
/** Left Arrow Style */
|
||||
leftArrowClassName?: string;
|
||||
/** 右箭头样式 */
|
||||
/** Right Arrow Style */
|
||||
rightArrowClassName?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -156,7 +156,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
itemsContainerRef?.current?.scrollLeft !== undefined &&
|
||||
itemsContainerRef?.current?.clientWidth
|
||||
) {
|
||||
// 部分浏览器不支持 scrollTo 方法
|
||||
// Some browsers do not support the scrollTo method
|
||||
itemsContainerRef.current.scrollTo?.({
|
||||
left: Math.max(
|
||||
itemsContainerRef.current.scrollLeft -
|
||||
@@ -193,7 +193,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
scrollLeft;
|
||||
|
||||
const shouldShowArrowLeft = scrollLeft > 0;
|
||||
// 极端场景下存在 1px 偏差
|
||||
// There is a 1px bias in extreme scenarios
|
||||
const shouldShowArrowRight = Math.abs(scrollRight) > 2;
|
||||
|
||||
setLeftArrowVisible(shouldShowArrowLeft);
|
||||
@@ -204,7 +204,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
updateArrowVisible();
|
||||
};
|
||||
|
||||
// 初始化时判读一次是否显示箭头
|
||||
// Determine whether to display arrows once during initialization
|
||||
updateArrowVisible();
|
||||
itemsContainerRef?.current?.addEventListener('scroll', scrollEvent);
|
||||
window?.addEventListener('resize', updateArrowVisible);
|
||||
|
||||
@@ -39,7 +39,7 @@ interface CollapsibleIconButtonContextValue {
|
||||
setItems: Dispatch<SetStateAction<ContextItems | undefined>>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- 这是 context
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is context
|
||||
export const CollapsibleIconButtonContext =
|
||||
createContext<CollapsibleIconButtonContextValue>({
|
||||
showText: true,
|
||||
@@ -56,7 +56,7 @@ export const useItem = (key: symbol, ref: RefObject<HTMLElement>) => {
|
||||
}));
|
||||
}, [size?.width]);
|
||||
|
||||
// 组件销毁后移除
|
||||
// Component destruction and removal
|
||||
useEffect(
|
||||
() => () => {
|
||||
setItems(items => omit(items, key));
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
|
||||
import { CollapsibleIconButtonContext, useWrapper, useItem } from './context';
|
||||
|
||||
/** 能让 Group 内的所有 CollapsibleIconButton 根据空余宽度自动展开(露出文案)收起(隐藏文案只剩图标) */
|
||||
/** Can make all the CollapsibleIconButton in the Group automatically expand according to the free width (expose the copy) and put away (hide the copy and only the icon is left) */
|
||||
export const CollapsibleIconButtonGroup: FC<
|
||||
PropsWithChildren<{
|
||||
/** @default 12 */
|
||||
@@ -67,12 +67,12 @@ export const CollapsibleIconButton = forwardRef<
|
||||
|
||||
return (
|
||||
<span ref={ref}>
|
||||
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
|
||||
{/* Render to the outside of the screen when not visible for width */}
|
||||
<div className={showText ? '' : 'fixed left-[-999px]'} ref={contentRef}>
|
||||
<Button
|
||||
size="default"
|
||||
color="secondary"
|
||||
// 不可见时不附带 testid,避免对 E2E 产生影响
|
||||
// Invisible without testid to avoid impact on E2E
|
||||
{...(showText ? rest : omit(rest, 'data-testid'))}
|
||||
>
|
||||
{text}
|
||||
@@ -87,7 +87,7 @@ export const CollapsibleIconButton = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/** 更为通用的版本 */
|
||||
/** A more general version */
|
||||
export const Collapsible = forwardRef<
|
||||
HTMLSpanElement,
|
||||
{
|
||||
@@ -102,7 +102,7 @@ export const Collapsible = forwardRef<
|
||||
|
||||
return (
|
||||
<span ref={ref}>
|
||||
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
|
||||
{/* Render to the outside of the screen when not visible for width */}
|
||||
<div className={showFull ? '' : 'fixed left-[-999px]'} ref={contentRef}>
|
||||
{fullContent}
|
||||
</div>
|
||||
@@ -118,7 +118,7 @@ export const Collapsible = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/** 不会折叠,但参与宽度计算的元素 */
|
||||
/** Elements that do not collapse, but participate in width calculations */
|
||||
export function PlaceholderContainer({
|
||||
itemKey,
|
||||
children,
|
||||
|
||||
@@ -57,7 +57,7 @@ interface DuplicateBotProps {
|
||||
buttonSize?: Size;
|
||||
enableCozeDesign?: boolean;
|
||||
/**
|
||||
* cozeDesign 的情况下才生效
|
||||
* CozeDesign only takes effect in the case of
|
||||
*/
|
||||
isBlock?: boolean;
|
||||
eventCallbacks?: Partial<{
|
||||
@@ -143,17 +143,17 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
//复制完成,关闭空间弹窗
|
||||
//Copy complete, close the space pop-up window
|
||||
setShowSpaceModal(false);
|
||||
} else if (pageFromFromStore === BotPageFromEnum.Explore) {
|
||||
//explore时可以复制到某个空间下
|
||||
//When exploring, it can be copied to a certain space
|
||||
resp = await DeveloperApi.DuplicateBotToSpace({
|
||||
draft_bot_id: botIdFromStore,
|
||||
target_space_id: targetSpaceId || '',
|
||||
name,
|
||||
});
|
||||
|
||||
//复制完成,关闭空间弹窗
|
||||
//Copy complete, close the space pop-up window
|
||||
setShowSpaceModal(false);
|
||||
} else {
|
||||
resp = await SpaceApi.DuplicateDraftBot({
|
||||
@@ -260,14 +260,14 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
from: 'explore_card',
|
||||
source: 'explore_bot_detailpage',
|
||||
});
|
||||
//探索页面来源:team>1时选择copy 空间,否则copy到个人空间
|
||||
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
|
||||
if (list.length === 1) {
|
||||
openNewWindow(() => copyAndOpenBot(list?.[0].id));
|
||||
} else {
|
||||
setShowSpaceModal(true);
|
||||
}
|
||||
} else if (pageFrom === BotPageFromEnum.Template) {
|
||||
//探索页面来源:team>1时选择copy 空间,否则copy到个人空间
|
||||
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
|
||||
if (list.length === 1) {
|
||||
openNewWindow(() => copyAndOpenBot(list?.[0].id));
|
||||
} else {
|
||||
@@ -281,7 +281,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
source: 'bots_detailpage',
|
||||
from: 'bots_card',
|
||||
});
|
||||
// bot页面来源:若有操作权限直接copy到当前空间下
|
||||
// Bot page source: If there is operation permission, directly copy it to the current space
|
||||
if (hide_operation) {
|
||||
Toast.warning('Bot in public space cannot duplicate');
|
||||
return;
|
||||
@@ -316,7 +316,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
</UIButton>
|
||||
)}
|
||||
|
||||
{/* 选择空间弹窗 */}
|
||||
{/* Select space pop-up */}
|
||||
<SelectSpaceModal
|
||||
botName={botName ?? botNameFromStore}
|
||||
visible={showSpaceModal}
|
||||
|
||||
@@ -93,7 +93,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
|
||||
(scene === 'gif' && gifCount >= 10) ||
|
||||
(scene === 'static_image' && staticImageCount >= 20)
|
||||
) {
|
||||
// 达到上限,禁用按钮
|
||||
// Limit reached, disable button
|
||||
setExceedImageGenCountLimit(true);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
// 获取图片限制,每天限制10个gif,20个静态图,根据scene来判断是否达到上限
|
||||
// Get the picture limit, limit 10 gifs and 20 static pictures per day, and judge whether the upper limit is reached according to the scene.
|
||||
if (!loading) {
|
||||
getGenPicTimes();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function ImagePicker(props: ImagePickerProps) {
|
||||
maxSize={2 * 1024}
|
||||
onSizeError={() => {
|
||||
Toast.error({
|
||||
// starling 切换
|
||||
// Starling toggle
|
||||
content: withSlardarIdButton(
|
||||
I18n.t(
|
||||
'dataset_upload_image_warning',
|
||||
|
||||
@@ -35,11 +35,11 @@ import s from './index.module.less';
|
||||
|
||||
interface GenerateGifProps {
|
||||
scene: 'background' | 'avatar';
|
||||
image: ImageItem; // 默认图片
|
||||
text: string; // 默认文本
|
||||
loading: boolean; // 生成时,socket全局监听服务端响应,因此loading需要受控
|
||||
imageList?: ImageItem[]; // 图片候选列表(只包含静态图)
|
||||
generatingTaskId: string; // 生成中的任务id
|
||||
image: ImageItem; // default image
|
||||
text: string; // default text
|
||||
loading: boolean; // When generated, sockets listen globally for server level responses, so loading needs to be controlled
|
||||
imageList?: ImageItem[]; // Picture Candidate List (contains only static images)
|
||||
generatingTaskId: string; // Generating task id
|
||||
exceedMaxImageCount?: boolean;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
@@ -106,7 +106,7 @@ export const GenerateGif: React.FC<GenerateGifProps> = ({
|
||||
setLoading?.(true);
|
||||
try {
|
||||
avatarBackgroundWebSocket.createConnection();
|
||||
// 只负责发送请求,响应在WebSocket中接收
|
||||
// Only responsible for sending requests, responses are received in WebSocket
|
||||
const { data } = await PlaygroundApi.GeneratePic({
|
||||
gen_prompt: {
|
||||
ori_prompt: text.trim(),
|
||||
|
||||
@@ -31,15 +31,15 @@ import { type TabItem } from './type';
|
||||
import s from './index.module.less';
|
||||
|
||||
export interface GenerateImageTabProps {
|
||||
// tab列表
|
||||
// tab list
|
||||
tabs: TabItem[];
|
||||
// 是否可折叠
|
||||
// Is it foldable?
|
||||
enableCollapsible?: boolean;
|
||||
// 当前激活的tab
|
||||
// Currently active tab
|
||||
activeKey?: string;
|
||||
// 当前激活的tab变化回调
|
||||
// Currently active tab change callback
|
||||
onTabChange?: (tabKey: string) => void;
|
||||
// 是否展示wait文案
|
||||
// Whether to show the waiting copy
|
||||
showWaitTip?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
|
||||
setOpen(!isOpen);
|
||||
};
|
||||
|
||||
// tabPane 不卸载
|
||||
// tabPane does not uninstall
|
||||
const component = (
|
||||
<div>
|
||||
{tabs.map(item => (
|
||||
@@ -124,7 +124,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
|
||||
) : null}
|
||||
</div>
|
||||
{enableCollapsible ? (
|
||||
// keepDOM 异常失效使用collapseHeight 不销毁dom保留状态
|
||||
// keepDOM exception fails using collapseHeight does not destroy dom keep state
|
||||
<Collapsible isOpen={isOpen} keepDOM collapseHeight={1}>
|
||||
<div> {component} </div>
|
||||
</Collapsible>
|
||||
|
||||
@@ -28,8 +28,8 @@ import s from './index.module.less';
|
||||
export type ImageItem = PicTask;
|
||||
|
||||
export interface ImageListProps {
|
||||
selectedKey?: string; // 选中的key
|
||||
data: ImageItem[]; // 列表数据
|
||||
selectedKey?: string; // Selected key
|
||||
data: ImageItem[]; // list data
|
||||
className?: string;
|
||||
imageItemClassName?: string;
|
||||
showDeleteIcon?: boolean;
|
||||
@@ -39,18 +39,18 @@ export interface ImageListProps {
|
||||
index?: number;
|
||||
item?: ImageItem;
|
||||
data: ImageItem[];
|
||||
}) => void; // 删除图片,data是此次删除之后的数据
|
||||
}) => void; // Delete the picture, data is the data after this deletion.
|
||||
onSelect?: (params: {
|
||||
index?: number;
|
||||
item: ImageItem;
|
||||
data: ImageItem[];
|
||||
selected: boolean;
|
||||
}) => void; // 选中图片,其中item和data都是此次选中之前的数据,selected表示在本次选中之前此图片是否已是选中状态
|
||||
}) => void; // Select the picture, where item and data are the data before this selection. Selected indicates whether the picture was selected before this selection
|
||||
onClick?: (params: {
|
||||
index: number;
|
||||
item: ImageItem;
|
||||
data: ImageItem[];
|
||||
}) => void; // 点击图片
|
||||
}) => void; // Click on the picture.
|
||||
}
|
||||
|
||||
export const ImageList: React.FC<ImageListProps> = ({
|
||||
|
||||
@@ -56,7 +56,7 @@ export { UploadGenerateButton } from './upload-generate-button';
|
||||
|
||||
export { usePluginLimitModal, transPricingRules } from './plugin-limit-info';
|
||||
|
||||
// 曝光埋点上报组件,进入视图上报
|
||||
// Exposure event tracking report component, enter the view report
|
||||
export { TeaExposure } from './tea-exposure';
|
||||
export { Sticky } from './sticky';
|
||||
|
||||
@@ -67,6 +67,6 @@ export {
|
||||
appendCopySuffix,
|
||||
} from './project-duplicate-modal';
|
||||
export { SpaceFormSelect } from './space-form-select';
|
||||
// !Notice 以下模块只允许导出类型,避免首屏加载 react-dnd,@blueprintjs/core 等相关代码
|
||||
// ! Notice that the following modules only allow export types, avoid loading react-dnd, @blueprintjs/core and other related codes on the first screen
|
||||
export { type TItemRender, type ITemRenderProps } from './sortable-list';
|
||||
export { type ConnectDnd, type OnMove } from './sortable-list/hooks';
|
||||
|
||||
@@ -109,7 +109,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
|
||||
const onNumberChange = (numberValue: number) => {
|
||||
updateInputNumber();
|
||||
|
||||
// 防止 -0
|
||||
// Prevent -0
|
||||
if (numberValue === 0) {
|
||||
onChange?.(0);
|
||||
return;
|
||||
@@ -124,7 +124,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
|
||||
onChange?.(expectedFormattedValue);
|
||||
};
|
||||
|
||||
// 防止 -0 导致 InputNumber 无限循环更新
|
||||
// Prevent -0 from causing InputNumber to update indefinitely
|
||||
const fixedValue = Object.is(value, -0) ? 0 : value;
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -36,7 +36,7 @@ const LimitCount: React.FC<LimitCountProps> = ({ maxLen, len }) => (
|
||||
);
|
||||
|
||||
export interface InputWithCountProps extends InputProps {
|
||||
// 设置字数限制并显示字数统计
|
||||
// Set word limits and display word count
|
||||
getValueLength?: (value?: InputProps['value'] | string) => number;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export const ListTab: React.FC<PropsWithChildren<BotListHeaderProps>> = ({
|
||||
{...props}
|
||||
tabPaneMotion={false}
|
||||
type="button"
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- react 组件
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- react component
|
||||
renderTabBar={(innerProps, Node) => (
|
||||
<div className={cs(s.header, containerClass)}>
|
||||
<Node {...innerProps} />
|
||||
|
||||
@@ -22,7 +22,7 @@ import { type ButtonProps } from '@coze-arch/bot-semi/Button';
|
||||
import { UIButton, Toast, Spin } from '@coze-arch/bot-semi';
|
||||
|
||||
export type LoadingButtonProps = ButtonProps & {
|
||||
/** 加载中的 toast 文案 */
|
||||
/** Loading toast copy */
|
||||
loadingToast?: string | Omit<ToastReactProps, 'type'>;
|
||||
};
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ export const ActionBar: React.FC<ActionBarProps> = ({
|
||||
</span>
|
||||
</InsertLinkPopover>
|
||||
|
||||
{/* 暂时禁用,后续放开 */}
|
||||
{/* Temporarily disabled, later released */}
|
||||
{SHOW_ADD_NAME_BTN && (
|
||||
<Tooltip content={I18n.t('add_nickname')}>
|
||||
<UIButton
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface InsertLinkPopoverProps
|
||||
}
|
||||
|
||||
/**
|
||||
* 全受控
|
||||
* fully controlled
|
||||
*/
|
||||
export const InsertLinkPopover: React.FC<
|
||||
PropsWithChildren<InsertLinkPopoverProps>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const getSyncInsertText = (action: SyncAction): string => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 不应该走到这里
|
||||
* Shouldn't have come here
|
||||
*/
|
||||
primitiveExhaustiveCheck(type);
|
||||
return '';
|
||||
|
||||
@@ -78,7 +78,7 @@ export const useMarkdownEditor = ({
|
||||
},
|
||||
);
|
||||
|
||||
// 判断使用内置上传方法 or 自定义
|
||||
// Determine whether to use the built-in upload method or customize
|
||||
const selectUploadMethod = () => {
|
||||
if (customUpload) {
|
||||
return customUpload({
|
||||
@@ -143,7 +143,7 @@ export const useMarkdownEditor = ({
|
||||
ref.current.focus();
|
||||
const { selectionEnd } = ref.current;
|
||||
/**
|
||||
* 选中文字时点击 action bar, 将内容插入到文字的末尾
|
||||
* When the text is selected, click the action bar to insert the content at the end of the text
|
||||
*/
|
||||
console.log('handleInsertText', { value, insertText, selectionEnd });
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { type UploadState } from '../type';
|
||||
import { UploadController } from '../service/upload-controller';
|
||||
|
||||
/**
|
||||
* 暂时没有场景,所以这里将多实例、一次行上传多文件的能力屏蔽了
|
||||
* There is no scene for the time being, so the ability to upload multiple files with multiple instances and one line is blocked here.
|
||||
*/
|
||||
export const useUpload = ({
|
||||
getUserId,
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface MarkdownEditorProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全受控组件
|
||||
* fully controlled component
|
||||
*/
|
||||
export const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
|
||||
value = '',
|
||||
|
||||
@@ -50,13 +50,13 @@ export interface UploadState {
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
// 自定义上传方法入参
|
||||
// Custom upload method imported parameters
|
||||
export interface CustomUploadParams {
|
||||
onUploadAllSuccess: (param: { url: string; fileName: string }) => void;
|
||||
}
|
||||
// 自定义上传方法出参
|
||||
// Custom upload method exported parameter
|
||||
export interface CustomUploadRes {
|
||||
uploadFileList: (fileList: File[]) => void;
|
||||
//null表示已完成
|
||||
//Null means completed
|
||||
uploadState: UploadState | null;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ export const matchAllTemplateRanges = (
|
||||
text: string,
|
||||
template: string,
|
||||
): { start: number; end: number }[] => {
|
||||
// 正则表达式,用于匹配双花括号内的内容
|
||||
// Regular expressions to match the contents of double curly braces
|
||||
const templateRegex = new RegExp(getFixedVariableTemplate(template), 'g');
|
||||
const matches: { start: number; end: number }[] = [];
|
||||
|
||||
// 循环查找所有匹配项
|
||||
// Loop through all matches
|
||||
while (true) {
|
||||
const match = templateRegex.exec(text);
|
||||
|
||||
|
||||
@@ -53,12 +53,12 @@ export interface OptionItemProps {
|
||||
avatar: string | undefined;
|
||||
name: string | undefined;
|
||||
searchWords?: string[];
|
||||
endPointName?: string; // 接入点名称(专业版有)
|
||||
endPointName?: string; // Access point name (available in the professional version)
|
||||
showEndPointName?: boolean;
|
||||
className?: string;
|
||||
/**
|
||||
* @deprecated
|
||||
* 原先只会有「限额」标签,M-5395720900 后会有大量新标签,避免兼容问题产品同意先简单隐藏掉标签展示
|
||||
* Originally, there would only be "limit" labels. After M-5395720900, there will be a large number of new labels to avoid compatibility problems. The product agrees to simply hide the label display first.
|
||||
*/
|
||||
tags?: OptionItemTag[];
|
||||
}
|
||||
@@ -77,12 +77,12 @@ export const ModelOptionItem: React.FC<OptionItemProps> = ({
|
||||
const tags: OptionItemTag[] = [];
|
||||
|
||||
const shouldShowEndPoint = showEndPointName && endPointName;
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
const displayName = FLAGS['bot.studio.model_select_switch_end_point_name_pos']
|
||||
? endPointName || name
|
||||
: name;
|
||||
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
const displayEndPointName = FLAGS[
|
||||
'bot.studio.model_select_switch_end_point_name_pos'
|
||||
]
|
||||
|
||||
@@ -23,11 +23,11 @@ import { MonetizeDescription } from '../monetize-description';
|
||||
import { MonetizeCreditRefreshCycle } from '../monetize-credit-refresh-cycle';
|
||||
|
||||
export interface MonetizeConfigValue {
|
||||
/** 是否开启付费 */
|
||||
/** Whether to start payment */
|
||||
isOn: boolean;
|
||||
/** 开启付费后,用户免费体验的次数 */
|
||||
/** The number of free user experiences after starting payment */
|
||||
freeCount: number;
|
||||
/** 刷新周期 */
|
||||
/** refresh cycle */
|
||||
refreshCycle: BotMonetizationRefreshPeriod;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ interface MonetizeConfigPanelProps {
|
||||
value: MonetizeConfigValue;
|
||||
onChange: (value: MonetizeConfigValue) => void;
|
||||
/**
|
||||
* 内置防抖后的 onChange 事件,业务侧可选择性使用,正常只传 onChange 即可
|
||||
* (由于该组件是完全受控组件,因此不能只传 onDebouncedChange,必须传 onChange 实时更新视图)
|
||||
* The onChange event after built-in anti-shake can be selectively used by the business side. Normally, only onChange can be transmitted.
|
||||
* (Since this component is a fully controlled component, you cannot just pass onDebouncedChange, you must pass onChange to update the view in real time)
|
||||
*/
|
||||
onDebouncedChange?: (value: MonetizeConfigValue) => void;
|
||||
}
|
||||
|
||||
@@ -32,23 +32,23 @@ import s from './index.module.less';
|
||||
|
||||
interface MenuItem {
|
||||
/**
|
||||
* 如果是string,需传入starling key,并且会由div包一层
|
||||
* 如果是function,则自定义label的实现,active表示是否是选中态
|
||||
* If it is a string, you need to pass in the starling key, and it will be wrapped by the div layer.
|
||||
* If it is a function, the implementation of the custom label, active indicates whether it is selected
|
||||
*/
|
||||
label: string | ((active: boolean) => React.ReactNode);
|
||||
/** label 外的 badge,未来再扩展配置项 */
|
||||
/** The badge outside the label, and the configuration item will be expanded in the future */
|
||||
badge?: string;
|
||||
app: SpaceAppEnum;
|
||||
/**
|
||||
* Q:为什么不叫 visible?FG 要取反,filter() 也要取反,很麻烦
|
||||
* A:为了兼容旧配置,缺省时认定为 visible。避免合码时无冲突 导致忽略掉新增配置的问题。
|
||||
* Q: Why is it not called visible? FG should be reversed, and filter () should also be reversed, which is very troublesome.
|
||||
* A: In order to be compatible with the old configuration, it is recognized as visible by default. To avoid no conflicts when combining codes, the problem of new configurations is ignored.
|
||||
*/
|
||||
invisible?: boolean;
|
||||
/** 目前(24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
|
||||
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
|
||||
icon?: ReactNode;
|
||||
/** 目前(24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
|
||||
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
|
||||
selectedIcon?: ReactNode;
|
||||
/** 自动化打标 */
|
||||
/** Automatic marking */
|
||||
e2e?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import React, {
|
||||
import classNames from 'classnames';
|
||||
|
||||
import s from './index.module.less';
|
||||
// react-markdown 20ms 左右的 longtask
|
||||
// React-markdown longtask around 20ms
|
||||
const LazyReactMarkdown = lazy(() => import('react-markdown'));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const ReactMarkdown = (props: any) => (
|
||||
|
||||
@@ -94,10 +94,10 @@ export type ProjectTemplateCopySuccessCallback = (param: {
|
||||
|
||||
export const useProjectTemplateCopyModal = (props: {
|
||||
modalTitle: string;
|
||||
/** 是否需要选择 space */
|
||||
/** Do you need to choose space? */
|
||||
isSelectSpace: boolean;
|
||||
onSuccess?: ProjectTemplateCopySuccessCallback;
|
||||
/** 埋点参数 - 当前页面/来源 */
|
||||
/** Event tracking parameters - current page/source */
|
||||
source: NonNullable<
|
||||
ParamsTypeDefine[EVENT_NAMES.template_action_front]['source']
|
||||
>;
|
||||
@@ -167,7 +167,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
formProps={{
|
||||
initValues,
|
||||
onValueChange: val => {
|
||||
// 当用户删除 input 中所有字符时,val.name 字段会消失,而不是空字符串,神秘
|
||||
// When the user removes all characters in input, val.name field disappears instead of empty string
|
||||
setIsFormValid(!!val.name?.trim());
|
||||
},
|
||||
getFormApi: api => {
|
||||
@@ -181,7 +181,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
sourceProduct: inputSourceProduct,
|
||||
}: {
|
||||
initValue: ProjectTemplateCopyValue;
|
||||
/** 用于提取埋点参数 */
|
||||
/** Used to extract event tracking parameters */
|
||||
sourceProduct: ProductInfo;
|
||||
}) => {
|
||||
setInitValues({
|
||||
|
||||
@@ -26,7 +26,7 @@ import classnames from 'classnames';
|
||||
|
||||
import s from './handle.module.less';
|
||||
|
||||
// 目前只支持水平方向,按需扩展吧
|
||||
// Currently only supports horizontal direction, expand it on demand.
|
||||
export interface ResizableLayoutHandleProps {
|
||||
className?: string;
|
||||
hotZoneClassName?: string;
|
||||
@@ -94,7 +94,7 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
|
||||
|
||||
const offEvents = () => {
|
||||
window.removeEventListener('pointermove', move, false);
|
||||
// 适配移动端出现多点触控的情况
|
||||
// Adapt to the situation of multi-touch on the mobile end
|
||||
window.removeEventListener('pointerdown', moveEnd, false);
|
||||
window.removeEventListener('pointerup', moveEnd, false);
|
||||
window.removeEventListener('pointercancel', moveEnd, false);
|
||||
@@ -108,12 +108,12 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
|
||||
setMoving(true);
|
||||
callbackRef.current.onMoveStart();
|
||||
window.addEventListener('pointermove', move, false);
|
||||
// 适配移动端出现多点触控的情况
|
||||
// Adapt to the situation of multi-touch on the mobile end
|
||||
window.addEventListener('pointerdown', moveEnd, false);
|
||||
window.addEventListener('pointerup', moveEnd, false);
|
||||
window.addEventListener('pointercancel', moveEnd, false);
|
||||
};
|
||||
// TODO hover 样式 & 热区宽度需要和 UI 对齐
|
||||
// TODO hover style & hotzone width needs to be aligned with UI
|
||||
return (
|
||||
<div
|
||||
className={classnames(hotZoneStyle, hotZoneClassName)}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {
|
||||
Children,
|
||||
type PropsWithChildren,
|
||||
@@ -61,7 +61,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
return;
|
||||
}
|
||||
const totalSize = sum(state.itemWidth);
|
||||
// 排除还没有进行过拖拽的情况,此时本地 state 中没有记录上次分配的宽度
|
||||
// Exclude the case where no drag has been performed, and the last allocated width is not recorded in the local state at this time
|
||||
if (totalSize <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
// @ts-expect-error -- 跳过类型体操
|
||||
// @ts-expect-error -- skip type gymnastics
|
||||
const { ref } = child;
|
||||
if (typeof ref === 'function') {
|
||||
ref(target);
|
||||
@@ -144,7 +144,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
),
|
||||
});
|
||||
}}
|
||||
// 相对于初始位置的偏移量
|
||||
// Offset from the initial position
|
||||
onMove={offset => {
|
||||
const pre = index - 1;
|
||||
childRef.current[pre].style.width = `${
|
||||
@@ -156,7 +156,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
}}
|
||||
onMoveEnd={() => {
|
||||
setState({
|
||||
// 拖拽结束后,记录真实宽度
|
||||
// After dragging, record the true width
|
||||
itemWidth: childRef.current.map(
|
||||
item => item.clientWidth ?? 0,
|
||||
),
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './index.module.less';
|
||||
|
||||
export interface SearchProps {
|
||||
defaultValue?: string;
|
||||
/** 当此值变化时,更新内部的搜索内容 */
|
||||
/** When this value changes, update the internal search content */
|
||||
refreshValue?: string;
|
||||
onSearch?: (value?: string) => void;
|
||||
placeholder?: string;
|
||||
|
||||
@@ -106,7 +106,7 @@ export const SelectSpaceModal: React.FC<
|
||||
const params = form.current?.formApi.getValues();
|
||||
await onConfirm?.(params?.spaceId ?? '', params?.name);
|
||||
} catch {
|
||||
// 审核不通过会走到这个逻辑,调用接口处会上报自定义异常
|
||||
// If the review fails, you will go to this logic, and a custom exception will be reported at the calling interface.
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -21,7 +21,7 @@ export type OnMove<TId = string | number> = (
|
||||
souceId: TId,
|
||||
targetId: TId,
|
||||
isBefore: boolean,
|
||||
) => void; // 因为没有顺序信息,所以需要指明是在前面还是后面
|
||||
) => void; // Since there is no order information, it is necessary to specify whether it is before or after
|
||||
export interface UseDndSortableParams<TId = string | number> {
|
||||
id: TId;
|
||||
type: symbol;
|
||||
@@ -61,7 +61,7 @@ export const useDnDSortableItem = <TId = string | number>({
|
||||
if (!itemDomRef.current || item.id === id) {
|
||||
return;
|
||||
}
|
||||
// 当前被拖拽元素的 X Y 坐标
|
||||
// The X Y coordinates of the currently dragged element
|
||||
const draggingClient = monitor.getClientOffset();
|
||||
const dropTargetClient = itemDomRef.current?.getBoundingClientRect();
|
||||
let isBefore = false;
|
||||
|
||||
@@ -87,7 +87,7 @@ export const SortableList = <TData extends object>({
|
||||
enabled={enabled}
|
||||
direction={direction}
|
||||
/**
|
||||
* 原始数组 [1, 2, target, 4, 5, source, 7, 8]
|
||||
* Raw array [1, 2, target, 4, 5, source, 7, 8]
|
||||
* before = true j==> [1,2, source, target, 4, 5, 7, 8]
|
||||
* before = false ==> [1,2, target, source, 4, 5, 7, 8]
|
||||
**/
|
||||
@@ -101,7 +101,7 @@ export const SortableList = <TData extends object>({
|
||||
newList.findIndex(target => getId(target) === targetId) +
|
||||
(before ? 0 : 1);
|
||||
if (sourceIndex === targetIndex) {
|
||||
// 前后 index 相同的情况不触发 onChange 避免频繁 rerender
|
||||
// Do not trigger onChange if the index is the same before and after to avoid frequent rerender
|
||||
return;
|
||||
}
|
||||
sourceItem && newList.splice(targetIndex, 0, sourceItem);
|
||||
|
||||
@@ -58,7 +58,7 @@ export const BaseSpaceFormSelect = withField(
|
||||
return;
|
||||
}
|
||||
|
||||
// 需要触发表单 onChange 事件 否则上层响应不到数据变化的事件
|
||||
// The form onChange event needs to be triggered, otherwise the upper layer will not respond to the data change event.
|
||||
props.onChange?.(fixedInitValue);
|
||||
}, [fixedInitValue]);
|
||||
|
||||
|
||||
@@ -20,24 +20,24 @@ import cls from 'classnames';
|
||||
import { useScroll } from 'ahooks';
|
||||
|
||||
interface StickyProps {
|
||||
/** 滚动容器,也就是那个 scrollHeight 大于 viewportHeight,overflow-y auto/scroll 的容器 */
|
||||
/** Scroll container, that is, the container with scrollHeight greater than viewportHeight and overflow-y auto/scroll */
|
||||
scrollContainerRef: () => Element;
|
||||
/**
|
||||
* 作用同 css sticky 时的 top 属性
|
||||
* The top property when used as a css sticky
|
||||
* @default 0
|
||||
*/
|
||||
top?: number;
|
||||
/**
|
||||
* 触发 sticky 后,底部(为了美观)额外的滚动距离
|
||||
* After triggering sticky, the bottom (for aesthetics) has an extra scroll distance
|
||||
* @default 0
|
||||
*/
|
||||
bottom?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* sticky 容器组件,用于解决 sticky 元素高于视窗时无法全部露出的问题
|
||||
* Sticky container component, used to solve the problem that sticky elements cannot be fully exposed when they are higher than the viewport
|
||||
*
|
||||
* 效果是触发 sticky 后,sticky 容器会跟随滚动容器的滚动而有限地上下移动
|
||||
* The effect is that after triggering the sticky, the sticky container will follow the scroll of the scrolling container and move up and down limited.
|
||||
*/
|
||||
export function Sticky({
|
||||
top: stickyTop = 0,
|
||||
@@ -46,11 +46,11 @@ export function Sticky({
|
||||
children,
|
||||
}: PropsWithChildren<StickyProps>) {
|
||||
const stickyContainerRef = useRef<HTMLDivElement>(null);
|
||||
/** 一个不可见的元素,用于通过 IntersectionObserver 检测是否已经 sticky */
|
||||
/** An invisible element used to detect whether it is sticky by IntersectionObserver */
|
||||
const stickyDetectRef = useRef<HTMLDivElement>(null);
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const prevScrollTop = useRef(scrollContainerRef()?.scrollTop || 0);
|
||||
// sticky 容器模拟向上滚动的距离
|
||||
// Sticky container simulates the distance of scrolling up
|
||||
const [simulateScrollDistance, setSimulateScrollDistance] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -58,7 +58,7 @@ export function Sticky({
|
||||
return;
|
||||
}
|
||||
|
||||
/** IntersectionObserver 监听是否触发 sticky */
|
||||
/** IntersectionObserver monitor if sticky is triggered */
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
const { isIntersecting } = entry;
|
||||
@@ -73,34 +73,34 @@ export function Sticky({
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 已测试该方法能监听 `scrollTo` 等方法产生的 scroll,无论模式是 smooth 还是 instant
|
||||
// It has been tested that this method can listen to scrolls generated by methods such as'scrollTo ', regardless of whether the mode is smooth or instant.
|
||||
useScroll(scrollContainerRef, scrollEvent => {
|
||||
/** 页面整体向上(scrollbar 向下移动)滚动的距离,为负则代表页面向下滚动 */
|
||||
/** The distance where the page scrolls up as a whole (scrollbar moves down). If it is negative, it means the page scrolls down. */
|
||||
const scrollUpDistance = scrollEvent.top - prevScrollTop.current;
|
||||
prevScrollTop.current = scrollEvent.top;
|
||||
|
||||
if (!stickyContainerRef.current || !isSticky) {
|
||||
// return false 避免 useScroll 产生 rerender,下同
|
||||
// (回调内的其他 setState 会正常触发 rerender)
|
||||
// Return false to avoid useScroll rerender
|
||||
// (Other setStates within the callback will trigger rerender normally)
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewportHeight = window.innerHeight;
|
||||
const stickyContainerHeight = stickyContainerRef.current?.scrollHeight || 0;
|
||||
|
||||
/** 触发 sticky 后,模拟滚动的容器高度 */
|
||||
/** After triggering sticky, simulate the height of the rolling container */
|
||||
const simulateStickyContainerHeight =
|
||||
stickyContainerHeight + stickyTop + stickyBottom;
|
||||
// 判断高度是否小于 viewport,是的话始终能正常显示在视图内,不用后面乱七八糟一堆计算了
|
||||
// Determine whether the height is less than the viewport. If so, it can always be displayed in the view normally, and there is no need to make a mess of calculations later.
|
||||
if (simulateStickyContainerHeight < viewportHeight) {
|
||||
return false;
|
||||
}
|
||||
/** 模拟滚动容器比视窗高出的部分,也即模拟滚动的上限 */
|
||||
/** The part of the simulated scrolling container higher than the viewport, that is, the upper limit of the simulated scrolling */
|
||||
const simulateMaxScrollDistance =
|
||||
simulateStickyContainerHeight - viewportHeight;
|
||||
|
||||
if (scrollUpDistance > 0) {
|
||||
// #region 处理向上滚动
|
||||
// #region processing scroll up
|
||||
const stickyReachedBottom =
|
||||
simulateScrollDistance >= simulateMaxScrollDistance;
|
||||
if (stickyReachedBottom) {
|
||||
@@ -116,7 +116,7 @@ export function Sticky({
|
||||
return false;
|
||||
// #endregion
|
||||
} else {
|
||||
// #region 处理向下滚动
|
||||
// #region processing scroll down
|
||||
const stickyReachedTop = simulateScrollDistance <= 0;
|
||||
if (stickyReachedTop) {
|
||||
setSimulateScrollDistance(0);
|
||||
|
||||
@@ -56,7 +56,7 @@ export const TableSelectAllPopover: FC<
|
||||
>
|
||||
{I18n.t('publish_permission_control_page_remove_choose_all')}
|
||||
</Checkbox>
|
||||
{/* 确保全选和右侧区域有一个最小间隔 */}
|
||||
{/* Make sure there is a minimum interval between Select All and the right area */}
|
||||
<div className="flex-1 min-w-[40px]" />
|
||||
{renderCount ? (
|
||||
<div>
|
||||
|
||||
@@ -28,12 +28,12 @@ import {
|
||||
type ParamsTypeDefine,
|
||||
} from '@coze-arch/bot-tea';
|
||||
|
||||
/** 后续考虑通用化封装,增加诸如延迟时间、露出比例等参数 */
|
||||
/** Subsequent consideration of generalized packaging, adding parameters such as delay time and exposure ratio */
|
||||
type TeaExposureProps<TEventName extends EVENT_NAMES> = {
|
||||
/**
|
||||
* 是否只上报一次
|
||||
* Is it only reported once?
|
||||
* @default false
|
||||
* @todo 有余力再考虑兼容虚拟滚动
|
||||
* @Todo has the spare time to reconsider compatibility with virtual scrolling
|
||||
*/
|
||||
once?: boolean;
|
||||
teaEvent: {
|
||||
@@ -44,13 +44,13 @@ type TeaExposureProps<TEventName extends EVENT_NAMES> = {
|
||||
HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* 曝光埋点上报组件
|
||||
* 可以当成普通的 div 来用(比如配置样式)
|
||||
* Exposure event tracking report component
|
||||
* It can be used as a normal div (e.g. configuration style).
|
||||
*
|
||||
* 封装的意义:避免组件 rerender
|
||||
* The meaning of encapsulation: avoid component rerendering
|
||||
*
|
||||
* useInViewport 会造成组件频繁 rerender,哪怕你并未使用其返回值,只要调用了这个 hook,就会随着其内部的 setState 触发 rerender。
|
||||
* 而我们都知道,对于下面这段代码,A rerender 会触发 B 和 C 的rerender;而 B rerender 不会触发 A 和 C 的 rerender。
|
||||
* useInViewport causes the component to rerender frequently, even if you don't use its return value. As long as this hook is called, rerender will be triggered with its internal setState.
|
||||
* And we all know that for the following code, A rerender will trigger the rerender of B and C, while B rerender will not trigger the rerender of A and C.
|
||||
* ```
|
||||
* const A = () => <B><C /></B>
|
||||
* ```
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.stubGlobal('SAMI_APP_KEY', vi.fn());
|
||||
vi.stubGlobal('IS_DEV_MODE', false);
|
||||
vi.stubGlobal('IS_OVERSEA', false);
|
||||
|
||||
// Mock Canvas API 完整版本
|
||||
// Mock Canvas API full version
|
||||
const createMockCanvas = () => ({
|
||||
getContext: vi.fn(() => ({
|
||||
fillRect: vi.fn(),
|
||||
@@ -82,14 +82,14 @@ if (global.document) {
|
||||
});
|
||||
}
|
||||
|
||||
// CSS 文件 mock
|
||||
// CSS file mock
|
||||
vi.mock('*.css', () => ({}));
|
||||
vi.mock('*.scss', () => ({}));
|
||||
vi.mock('*.sass', () => ({}));
|
||||
vi.mock('*.less', () => ({}));
|
||||
vi.mock('*.styl', () => ({}));
|
||||
|
||||
// Mock lottie-web 完整版本
|
||||
// Mock lottie-web full version
|
||||
vi.mock('lottie-web', () => ({
|
||||
default: {
|
||||
loadAnimation: vi.fn(() => ({
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
export interface CreateAgentEntityProps {
|
||||
onBefore?: () => void;
|
||||
onError?: () => void;
|
||||
/** 只有需要 onSuccess 回调阻断弹窗自动关闭时,才需要返回 Promise */
|
||||
/** Return Promise only if you need the onSuccess callback to block the pop-up window from closing automatically. */
|
||||
onSuccess?: (
|
||||
botId?: string,
|
||||
spaceId?: string,
|
||||
@@ -51,13 +51,13 @@ export interface CreateAgentEntityProps {
|
||||
mode: 'update' | 'add';
|
||||
showSpace?: boolean;
|
||||
/**
|
||||
* 需要从外部控制在哪个空间创建 bot 时,传入此参数
|
||||
* 仅适用于创建
|
||||
* Pass this parameter when you need to control externally which space to create the bot in
|
||||
* Only suitable for creating
|
||||
*/
|
||||
spaceId?: string;
|
||||
/**
|
||||
* navi 导航栏
|
||||
* space workspace 右上角的按钮
|
||||
* Navigation bar
|
||||
* Button in the upper right corner of the space workspace
|
||||
* */
|
||||
bizCreateFrom?: 'navi' | 'space';
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export const useCreateOrUpdateAgent = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
mode,
|
||||
showSpace = false, // 默认不显示
|
||||
showSpace = false, // Not displayed by default
|
||||
spaceId: outerSpaceId,
|
||||
bizCreateFrom,
|
||||
}: CreateAgentEntityProps) => {
|
||||
@@ -149,7 +149,7 @@ export const useCreateOrUpdateAgent = ({
|
||||
}, [visible]);
|
||||
|
||||
/**
|
||||
* @param _ 开源版本暂不支持此参数
|
||||
* @Param _ open source version does not support this parameter
|
||||
*/
|
||||
const startEdit = (_?: boolean) => {
|
||||
setVisible(true);
|
||||
|
||||
@@ -56,7 +56,7 @@ const mockSetInfoRules: {
|
||||
message: I18n.t('create_plugin_modal_nameerror'),
|
||||
}
|
||||
: {
|
||||
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // 国内增加支持中文
|
||||
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // Increased domestic support for Chinese
|
||||
message: I18n.t('create_plugin_modal_nameerror_cn'),
|
||||
},
|
||||
],
|
||||
@@ -115,10 +115,10 @@ export const MockSetEditModal = ({
|
||||
}: MockSetEditModalProps) => {
|
||||
const formApiRef = useRef<FormApi<EditMockSetInfo>>();
|
||||
|
||||
// 根据是否传入 id 判断是否为创建场景
|
||||
// Determine whether to create a scene based on whether to pass in the id
|
||||
const isCreate = !initialInfo.id;
|
||||
|
||||
// space信息
|
||||
// Space information
|
||||
const spaceType = useSpaceStore(s => s.space.space_type);
|
||||
const isPersonal = spaceType === SpaceType.Personal;
|
||||
|
||||
@@ -213,7 +213,7 @@ export const MockSetEditModal = ({
|
||||
>
|
||||
{({ formState }) => (
|
||||
<>
|
||||
{/* mockSet名称 */}
|
||||
{/* mockSet name */}
|
||||
{disabled ? (
|
||||
<Form.Slot
|
||||
label={{
|
||||
@@ -241,7 +241,7 @@ export const MockSetEditModal = ({
|
||||
rules={mockSetInfoRules.name}
|
||||
/>
|
||||
)}
|
||||
{/* mockSet描述 */}
|
||||
{/* mockSet description */}
|
||||
{disabled ? (
|
||||
<Form.Slot
|
||||
label={{
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
.editor-container_disabled {
|
||||
:global {
|
||||
.monaco-editor.no-user-select .view-lines {
|
||||
/** 只读状态下 cursor 纠正为正常鼠标 **/
|
||||
/** Correct cursor to normal mouse in read-only state **/
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@@ -34,23 +34,23 @@ export enum MockDataStatus {
|
||||
export interface MockDataWithStatus {
|
||||
/** key */
|
||||
key: string;
|
||||
/** 字段名称 */
|
||||
/** field name */
|
||||
label: string;
|
||||
/** 字段值 */
|
||||
/** field value */
|
||||
realValue?: string | number | boolean;
|
||||
/** 展示使用 */
|
||||
/** display use */
|
||||
displayValue?: string;
|
||||
/** 描述 */
|
||||
/** describe */
|
||||
description?: string;
|
||||
/** 是否必填 */
|
||||
/** Is it required? */
|
||||
isRequired: boolean;
|
||||
/** 字段数据类型 */
|
||||
/** field data type */
|
||||
type: MockDataValueType;
|
||||
/** for array */
|
||||
childrenType?: MockDataValueType;
|
||||
/** 字段状态 */
|
||||
/** Field Status */
|
||||
status: MockDataStatus;
|
||||
/** 字段子节点 */
|
||||
/** Field sub-node */
|
||||
children?: MockDataWithStatus[];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import type { JSONSchema7, JSONSchema7TypeName } from 'json-schema';
|
||||
import {
|
||||
TrafficScene,
|
||||
@@ -56,9 +56,9 @@ export const calcStringSize = (str: string) => {
|
||||
const { size } = new Blob([str]);
|
||||
return size;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugin resp 的类型由用户定义,包含任何可能
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- The type of the plugin resp is user-defined and contains any possible
|
||||
type PluginRespType = any;
|
||||
// 转换 DataWithStatus 格式到 Object 格式
|
||||
// Convert DataWithStatus format to Object format
|
||||
export function transDataWithStatus2Object(
|
||||
data: MockDataWithStatus,
|
||||
excludeRemovedItem?: boolean,
|
||||
@@ -128,7 +128,7 @@ export function getMockValue(
|
||||
}
|
||||
}
|
||||
|
||||
// 由 schema 生成 DataWithStatus 时不同类型的生成逻辑
|
||||
// Different types of generation logic when generating DataWithStatus from schema
|
||||
function getInitialValue(
|
||||
type: MockDataValueType,
|
||||
): [string | number | boolean | undefined, string | undefined] {
|
||||
@@ -144,7 +144,7 @@ function getSchemaType(type?: JSONSchema7TypeName | JSONSchema7TypeName[]) {
|
||||
return val === 'null' ? undefined : (val as MockDataValueType);
|
||||
}
|
||||
|
||||
// 转换 schema 格式到 DataWithStatus 格式(包含初始化逻辑)
|
||||
// Convert schema format to DataWithStatus format (including initialization logic)
|
||||
// eslint-disable-next-line complexity
|
||||
export function transSchema2DataWithStatus(
|
||||
label: string,
|
||||
@@ -223,7 +223,7 @@ export function transSchema2DataWithStatus(
|
||||
return item;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 内容结构依赖用户定义的 plugin resp 结构,包含任何可能
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- The content structure depends on the user-defined plugin resp structure, including any possible
|
||||
export function stringifyEditorContent(value: any) {
|
||||
return JSON.stringify(value, null, FORMAT_SPACE_SETTING);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getExpirationTime } from '@/utils/time';
|
||||
|
||||
export const columnExpireAtConf: () => ColumnProps<PersonalAccessToken> =
|
||||
() => ({
|
||||
title: I18n.t('expire_time_1'), // 状态
|
||||
title: I18n.t('expire_time_1'), // state
|
||||
dataIndex: 'expire_at',
|
||||
render: (expireTime: number) => getExpirationTime(expireTime),
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ interface ResultProps {
|
||||
onOk: () => void;
|
||||
}
|
||||
|
||||
// 新建编辑 PAT
|
||||
// New Edit PAT
|
||||
export const ResultModal = ({ visible, onOk, data }: ResultProps) => {
|
||||
const doCopyAsync = useMemoizedFn(() => {
|
||||
const targetKey = data?.token;
|
||||
|
||||
@@ -18,10 +18,10 @@ import dayjs from 'dayjs';
|
||||
import { I18n, type I18nKeysNoOptionsType } from '@coze-arch/i18n';
|
||||
|
||||
const MAX_EXPIRATION_DAYS = 30;
|
||||
// 1-30天有效期
|
||||
// 1-30 days valid period
|
||||
export const disabledDate = (date?: Date) => {
|
||||
const today = dayjs().startOf('day'); // 当天的开始时间
|
||||
const thirtyDaysLater = today.add(MAX_EXPIRATION_DAYS, 'day'); // 30天后的日期
|
||||
const today = dayjs().startOf('day'); // Start time of the day
|
||||
const thirtyDaysLater = today.add(MAX_EXPIRATION_DAYS, 'day'); // Date in 30 days
|
||||
|
||||
return (
|
||||
dayjs(date).isBefore(today, 'day') ||
|
||||
|
||||
@@ -40,16 +40,16 @@ export default defineConfig(
|
||||
'src/components/**',
|
||||
'src/pages/**',
|
||||
'src/constants/**',
|
||||
'src/utils/public-private-keys.ts', // window 提供的 api
|
||||
'src/utils/docs.ts', // 线上未使用 仅 boe使用 即将删除
|
||||
'src/utils/time.ts', // dayjs api 的调用
|
||||
'src/utils/public-private-keys.ts', // Windows API
|
||||
'src/utils/docs.ts', // Unused online, only boe used, about to be deleted
|
||||
'src/utils/time.ts', // Dayjs API calls
|
||||
'src/utils//analytics/index.ts',
|
||||
'src/utils//analytics/chart.ts', // 有图表 dom 相关内容
|
||||
'src/hooks/pat/action/**', // 操作类 hook
|
||||
'src/hooks/oauth-app/action/**', // 操作类 hook
|
||||
'src/hooks/use-arcosite.ts', // 线上未使用 仅 boe使用
|
||||
'src/hooks/use-show-mask.ts', // 主要为获取 dom 的 scrollTop
|
||||
'src/hooks/use-docs-path.ts', // useNavigate 相关内容
|
||||
'src/hooks/pat/action/**', // Action class hook
|
||||
'src/hooks/oauth-app/action/**', // Action class hook
|
||||
'src/hooks/use-arcosite.ts', // Not used online, only boe used
|
||||
'src/hooks/use-show-mask.ts', // Mainly scrollTop for getting dom
|
||||
'src/hooks/use-docs-path.ts', // useNavigate related content
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -22,9 +22,9 @@ export enum Layout {
|
||||
}
|
||||
|
||||
export interface HeaderConfig {
|
||||
isShow?: boolean; //是否显示header, 默认是true
|
||||
isNeedClose?: boolean; //是否需要关闭按钮, 默认是true
|
||||
extra?: ReactNode | false; // 用于站位的,默认无
|
||||
isShow?: boolean; //Whether to display headers, the default is true
|
||||
isNeedClose?: boolean; //Whether you need the close button, the default is true.
|
||||
extra?: ReactNode | false; // For standing, default none
|
||||
}
|
||||
|
||||
export interface DebugProps {
|
||||
|
||||
@@ -34,12 +34,12 @@ export interface IWorkflow {
|
||||
export interface IProject {
|
||||
id: string;
|
||||
type: 'app' | 'bot';
|
||||
mode: 'draft' | 'release' | 'websdk' | 'audit'; // 草稿模式 | 发布模式 | webSdk发布
|
||||
mode: 'draft' | 'release' | 'websdk' | 'audit'; // Draft Mode | Publish Mode | webSdk Publish
|
||||
caller?: 'UI_BUILDER' | 'CANVAS';
|
||||
connectorId?: string;
|
||||
conversationName?: string; // project的话,必须填写
|
||||
conversationId?: string; // type 为bot的话,必须填写
|
||||
sectionId?: string; // type 为bot的话,必须填写
|
||||
conversationName?: string; // Project must be filled in
|
||||
conversationId?: string; // If the type is bot, it must be filled in
|
||||
sectionId?: string; // If the type is bot, it must be filled in
|
||||
name?: string;
|
||||
defaultName?: string;
|
||||
defaultIconUrl?: string;
|
||||
@@ -68,28 +68,28 @@ export interface IBuilderChatProps {
|
||||
eventCallbacks?: IEventCallbacks;
|
||||
userInfo: IChatFlowProps['userInfo'];
|
||||
areaUi: {
|
||||
isDisabled?: boolean; // 默认 false
|
||||
uploadable?: boolean; // 默认 true
|
||||
isNeedClearContext?: boolean; // 是否显示 clearContext按钮
|
||||
isNeedClearMessage?: boolean; // 是否显示 clearMessage按钮
|
||||
isDisabled?: boolean; // Default false
|
||||
uploadable?: boolean; // Default true
|
||||
isNeedClearContext?: boolean; // Whether to display the clearContext button
|
||||
isNeedClearMessage?: boolean; // Whether to display the clearMessage button
|
||||
|
||||
//isShowHeader?: boolean; // 默认 false
|
||||
//isShowFooter?: boolean; // 默认 false
|
||||
//isShowHeader?: boolean;//default false
|
||||
//isShowFooter?: boolean;//default false
|
||||
input?: {
|
||||
placeholder?: string;
|
||||
renderChatInputTopSlot?: (isChatError?: boolean) => React.ReactNode;
|
||||
isShow?: boolean; //默认 true
|
||||
isShow?: boolean; //Default true
|
||||
defaultText?: string;
|
||||
isNeedAudio?: boolean; // 是否需要语音输入,默认是false
|
||||
isNeedAudio?: boolean; // Whether voice input is required, the default is false
|
||||
isNeedTaskMessage?: boolean;
|
||||
};
|
||||
header?: HeaderConfig; // 默认是
|
||||
header?: HeaderConfig; // Default is
|
||||
footer?: FooterConfig;
|
||||
uiTheme?: 'uiBuilder' | 'chatFlow'; // uiBuilder 的主题
|
||||
uiTheme?: 'uiBuilder' | 'chatFlow'; // Theme for uiBuilder
|
||||
renderLoading?: () => React.ReactNode;
|
||||
};
|
||||
auth?: {
|
||||
type: 'external' | 'internal'; // 内部: cookie换token, 外部: internal
|
||||
type: 'external' | 'internal'; // Internal: cookie for token, external: internal
|
||||
token?: string;
|
||||
refreshToken?: () => Promise<string> | string;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 依赖 treeShaking 去除无关配置(Argus)
|
||||
* Dependency treeShaking Removes Extraneous Configuration (Argus)
|
||||
*/
|
||||
const sdkRegion = 'cn';
|
||||
export const iframeAppHost = '';
|
||||
@@ -28,7 +28,7 @@ export const openApiCdnUrlByRegion = IS_OVERSEA
|
||||
: // cp-disable-next-line
|
||||
'https://lf3-static.bytednsdoc.com/obj/eden-cn/rkzild_lgvj/ljhwZthlaukjlkulzlp/';
|
||||
|
||||
// 用户需要修改此处baseurl,用于开放平台接口的域名配置
|
||||
// The user needs to modify the baseurl here to open the domain name configuration of the API interface
|
||||
export const openApiHostByRegion =
|
||||
typeof location !== 'undefined' ? location.origin : 'https://api.xxx.com';
|
||||
export const openApiHostByRegionWithToken = openApiHostByRegion;
|
||||
|
||||
@@ -60,7 +60,7 @@ export const usePluginFormState = (): UsePluginFormStateReturn => {
|
||||
const [headerList, setHeaderList] = useState<commonParamSchema[]>([
|
||||
{ name: 'User-Agent', value: 'Coze/1.0' },
|
||||
]);
|
||||
// 合规审核结果
|
||||
// Compliance audit results
|
||||
const [isValidCheckResult, setIsValidCheckResult] = useState(true);
|
||||
const [pluginTypeCreationMethod, setPluginTypeCreationMethod] =
|
||||
useState<string>();
|
||||
|
||||
@@ -110,7 +110,7 @@ export const PluginForm: FC<{
|
||||
const [mainAuthType, setMainAuthType] = useState<number>(0);
|
||||
const [authType, setAuthType] = useState<number>(0);
|
||||
const [disableEditUrl, setDisableEditUrl] = useState<boolean>(false);
|
||||
// 合规审核结果
|
||||
// Compliance audit results
|
||||
const changeVal = () => {
|
||||
if (!isValidCheckResult) {
|
||||
setIsValidCheckResult(true);
|
||||
@@ -147,7 +147,7 @@ export const PluginForm: FC<{
|
||||
},
|
||||
);
|
||||
/**
|
||||
* 获取默认icon, 并设置到form中
|
||||
* Get the default icon, and set it to the form
|
||||
*/
|
||||
const getIcon = async () => {
|
||||
try {
|
||||
@@ -175,16 +175,16 @@ export const PluginForm: FC<{
|
||||
}
|
||||
if (!isCreate && editInfo) {
|
||||
/**
|
||||
* 以下的 useState 都是更新插件
|
||||
* The following useStates are all update plugins
|
||||
*/
|
||||
/**
|
||||
* service在本次需求中被拓展了二级菜单,service的一级菜单为1,二级菜单为0,1,2
|
||||
* 后端为了不改动历史逻辑,新增一个sub_auth_type的字段,去记录service下的二级菜单的值,但是authType依旧是一个length为1的数组,并且值为1
|
||||
* 因此前端需要手动判断auth_type为1的时候,把sub_auth_type塞进去,组成一个length为2的数组,才能让casdar菜单回填editInfo的内容
|
||||
* 5,6,7是给前端用的,用来判断选中的是哪个。5是apiKey、6是zero、7是oicd
|
||||
* 好问题来了。authType是一个数组,但是长度不固定:
|
||||
* 1. 当auth_type长度为1时,代表是service,内容有且只有1,此时sub_auth_type为0,1,2中的一个
|
||||
* 2. 当auth_type长度为2时,代表是oAuth,内容为[3,4]此时没有sub_auth_type
|
||||
* Service has been expanded to the secondary menu in this demand. The primary menu of service is 1, and the secondary menu is 0, 1, 2.
|
||||
* In order not to change the history logic, the backend adds a sub_auth_type field to record the value of the secondary menu under the service, but the authType is still an array of length 1 and the value is 1.
|
||||
* Therefore, when the front end needs to manually determine that the auth_type is 1, the sub_auth_type is stuffed in to form an array of length 2, so that the casdar menu can backfill the content of editInfo
|
||||
* 5, 6, and 7 are for the front end to determine which one is selected. 5 is apiKey, 6 is zero, and 7 is oicd.
|
||||
* Good question. AuthType is an array, but the length is not fixed:
|
||||
* When the length of the auth_type is 1, it means that it is a service, and the content has and only 1. At this time, the sub_auth_type is one of 0, 1, and 2
|
||||
* 2. When the auth_type length is 2, it means oAuth, and the content is [3,4]. There is no sub_auth_type at this time
|
||||
*/
|
||||
if (editInfo.meta_info?.auth_type?.at(0) === 1) {
|
||||
switch (editInfo.meta_info?.sub_auth_type) {
|
||||
@@ -220,7 +220,7 @@ export const PluginForm: FC<{
|
||||
`${editInfo.plugin_type}-${editInfo.creation_method}`,
|
||||
);
|
||||
|
||||
// 如果存在私网连接,则禁用编辑URL
|
||||
// Disable editing URLs if there is a private network connection
|
||||
if (
|
||||
editInfo?.meta_info?.private_link_id &&
|
||||
compareLevel === UserLevel.Enterprise &&
|
||||
@@ -234,7 +234,7 @@ export const PluginForm: FC<{
|
||||
}, [visible]);
|
||||
|
||||
const reset = () => {
|
||||
//重置插件
|
||||
//Reset plugin
|
||||
getIcon();
|
||||
setAuthType(0);
|
||||
setAuthType(0);
|
||||
@@ -244,20 +244,20 @@ export const PluginForm: FC<{
|
||||
setPluginTypeCreationMethod(undefined);
|
||||
};
|
||||
|
||||
/** 添加header */
|
||||
/** Add header */
|
||||
const addHeader = () => {
|
||||
setHeaderList(list => [...list, { name: '', value: '' }]);
|
||||
};
|
||||
/** 删除header */
|
||||
/** Delete header */
|
||||
const deleteHeader = (index: number) => {
|
||||
// 若为最后一个header,则只清空内容,不删除
|
||||
// If it is the last header, only empty the content, not delete it
|
||||
setHeaderList(list =>
|
||||
list.length <= 1
|
||||
? [{ name: '', value: '' }]
|
||||
: list.filter((_, i) => i !== index),
|
||||
);
|
||||
};
|
||||
/** 编辑header */
|
||||
/** Edit header */
|
||||
const editHeader = (index: number, header: commonParamSchema) => {
|
||||
setHeaderList(list => list.map((item, i) => (i === index ? header : item)));
|
||||
};
|
||||
@@ -267,17 +267,17 @@ export const PluginForm: FC<{
|
||||
if (!editInfo) {
|
||||
authTypeInitValue = [0];
|
||||
}
|
||||
// 是 OAuth 的情况
|
||||
// It's OAuth's case
|
||||
if (editInfo?.meta_info?.auth_type?.length === 2) {
|
||||
authTypeInitValue = editInfo?.meta_info?.auth_type;
|
||||
}
|
||||
// service & no auth
|
||||
else {
|
||||
// 不需要授权,自然没有sub_auth_type
|
||||
// No authorization required, naturally no sub_auth_type
|
||||
if (editInfo?.meta_info?.auth_type?.at(0) === 0) {
|
||||
authTypeInitValue = editInfo?.meta_info?.auth_type;
|
||||
}
|
||||
// service, 有sub_auth_type
|
||||
// Service, with sub_auth_type
|
||||
else {
|
||||
if (typeof editInfo?.meta_info?.sub_auth_type !== 'undefined') {
|
||||
authTypeInitValue = [
|
||||
@@ -316,7 +316,7 @@ export const PluginForm: FC<{
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* 插件URL */}
|
||||
{/* plugin URL */}
|
||||
{!disabled ? (
|
||||
<FormInput
|
||||
disabled={disableEditUrl}
|
||||
@@ -335,7 +335,7 @@ export const PluginForm: FC<{
|
||||
rules={disableEditUrl ? [] : formRuleList.url}
|
||||
/>
|
||||
) : null}
|
||||
{/* 插件Header */}
|
||||
{/* Plugin Header */}
|
||||
<Form.Slot
|
||||
className={s['header-list']}
|
||||
label={{
|
||||
@@ -424,7 +424,7 @@ export const PluginForm: FC<{
|
||||
</div>
|
||||
</Form.Slot>
|
||||
|
||||
{/* 授权方式 */}
|
||||
{/* Authorization method */}
|
||||
|
||||
<FormCascader
|
||||
disabled={disabled}
|
||||
@@ -455,7 +455,7 @@ export const PluginForm: FC<{
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 授权方式 - Service - Service Token / API Key */}
|
||||
{/* Authorization - Service - Service Token/API Key */}
|
||||
{mainAuthType === 1 && authType === 5 && (
|
||||
<>
|
||||
<Form.RadioGroup
|
||||
@@ -500,7 +500,7 @@ export const PluginForm: FC<{
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 服务端动态返回授权项 */}
|
||||
{/* Server level dynamic return authorization */}
|
||||
{/* Service - OIDC & OAuth - Standard Mode */}
|
||||
{extItems?.map((item, index) => {
|
||||
let formInfo: Record<string, any> = {};
|
||||
@@ -592,7 +592,7 @@ export const PluginForm: FC<{
|
||||
case 1:
|
||||
setAuthType(6);
|
||||
break;
|
||||
//@ts-expect-error 授权类型兼容
|
||||
// @ts-expect-error Authorization type compatible
|
||||
case 2:
|
||||
setAuthType(7);
|
||||
break;
|
||||
@@ -609,7 +609,7 @@ export const PluginForm: FC<{
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* 插件头像 */}
|
||||
{/* plugin avatar */}
|
||||
<PictureUpload
|
||||
noLabel
|
||||
disabled={disabled}
|
||||
@@ -621,7 +621,7 @@ export const PluginForm: FC<{
|
||||
onChange={changeVal}
|
||||
/>
|
||||
|
||||
{/* 插件名称/插件描述/url/插件类型 */}
|
||||
{/* Plugin name/plugin description/url/plugin type */}
|
||||
<>
|
||||
<FormTextArea
|
||||
disabled={disabled}
|
||||
@@ -662,7 +662,7 @@ export const PluginForm: FC<{
|
||||
onChange={changeVal}
|
||||
rules={formRuleList.desc}
|
||||
/>
|
||||
{/* 插件类型 */}
|
||||
{/* plugin type */}
|
||||
<Form.Slot
|
||||
label={{
|
||||
text: I18n.t('plugin_creation_method'),
|
||||
|
||||
@@ -43,7 +43,7 @@ export const formRuleList = {
|
||||
message: I18n.t('create_plugin_modal_nameerror'),
|
||||
}
|
||||
: {
|
||||
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // 国内增加支持中文
|
||||
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // Increased domestic support for Chinese
|
||||
message: I18n.t('create_plugin_modal_nameerror_cn'),
|
||||
},
|
||||
],
|
||||
@@ -52,7 +52,7 @@ export const formRuleList = {
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_descrip1_error'),
|
||||
},
|
||||
// 只有cn 线上才支持中文
|
||||
// Only cn online supports Chinese.
|
||||
IS_OVERSEA || IS_BOE
|
||||
? {
|
||||
// eslint-disable-next-line no-control-regex -- regex
|
||||
@@ -110,7 +110,7 @@ export interface AuthOption {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- any
|
||||
[key: string]: any;
|
||||
}
|
||||
/** 递归寻找auth选项下的输入项 */
|
||||
/** Recursively find the input under the auth option */
|
||||
export const findAuthTypeItem = (data: AuthOption[], targetKey = 0) => {
|
||||
for (const item of data) {
|
||||
if (item.value === targetKey) {
|
||||
@@ -168,7 +168,7 @@ export interface UsePluginSchameReturnValue {
|
||||
defaultRuntime: string;
|
||||
}
|
||||
|
||||
// 获取schame 和 runtime options
|
||||
// Get schame and runtime options
|
||||
export const usePluginSchame = (): UsePluginSchameReturnValue => {
|
||||
const [authOption, setAuthOption] = useState<AuthOption[]>([]);
|
||||
const [runtimeOptions, setRuntimeOptions] = useState<RuntimeOptionsType[]>(
|
||||
|
||||
@@ -70,7 +70,7 @@ import s from './index.module.less';
|
||||
const { Text, Paragraph } = Typography;
|
||||
|
||||
const stopPro = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation(); //阻止冒泡
|
||||
e.stopPropagation(); //Stop bubbling
|
||||
};
|
||||
|
||||
const exampleStatusConfig = {
|
||||
@@ -134,7 +134,7 @@ export const useGetToolColumns = (props: UseGetToolColumnsProps) => {
|
||||
customRender,
|
||||
} = props ?? {};
|
||||
|
||||
/** 是否开启api */
|
||||
/** Whether to open api */
|
||||
const openApi = async ({
|
||||
apiId,
|
||||
disabled,
|
||||
@@ -216,7 +216,7 @@ export const useGetToolColumns = (props: UseGetToolColumnsProps) => {
|
||||
color="grey"
|
||||
size="small"
|
||||
style={{
|
||||
/** 确保没有极端的闪动case */
|
||||
/** Make sure there are no extreme flashing cases */
|
||||
fontVariantNumeric: 'tabular-nums',
|
||||
}}
|
||||
>
|
||||
@@ -443,7 +443,7 @@ export const useGetToolColumns = (props: UseGetToolColumnsProps) => {
|
||||
visible={record.api_id === showDropdownItem?.api_id}
|
||||
render={
|
||||
<Dropdown.Menu className="px-[4px]">
|
||||
{/* 即将支持,敬请期待 */}
|
||||
{/* Support soon, so stay tuned. */}
|
||||
{FLAGS2['bot.devops.plugin_import_export'] ? (
|
||||
<Dropdown.Item
|
||||
disabled={
|
||||
@@ -459,7 +459,7 @@ export const useGetToolColumns = (props: UseGetToolColumnsProps) => {
|
||||
{I18n.t('code_snippet')}
|
||||
</Dropdown.Item>
|
||||
) : null}
|
||||
{/* 即将支持,敬请期待 */}
|
||||
{/* Support soon, so stay tuned. */}
|
||||
{FLAGS2['bot.devops.plugin_mockset'] ? (
|
||||
<Dropdown.Item
|
||||
className="rounded-[4px]"
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
export { useFetchKnowledgeBenefit } from './use-fetch-knowledge-benefit';
|
||||
export enum PremiumPaywallBannerScene {
|
||||
Knowledge, // 知识库场景
|
||||
Token, // 其余 Token 消耗场景
|
||||
Knowledge, // Knowledge base scenario
|
||||
Token, // Token consumption scenarios
|
||||
}
|
||||
|
||||
export function PremiumPaywallBanner(_props: {
|
||||
|
||||
@@ -15,27 +15,27 @@
|
||||
*/
|
||||
|
||||
export enum PremiumPaywallScene {
|
||||
// 创建新空间
|
||||
// Create a new space
|
||||
AddSpace,
|
||||
// 新模型体验
|
||||
// New model experience
|
||||
NewModel,
|
||||
// 付费用户模板
|
||||
// paid user template
|
||||
ProTemplate,
|
||||
// 添加空间成员
|
||||
// Add space member
|
||||
AddSpaceMember,
|
||||
// 协作
|
||||
// collaboration
|
||||
Collaborate,
|
||||
// 跨空间资源复制
|
||||
// Cross-spatial resource replication
|
||||
CopyResourceCrossSpace,
|
||||
// 发布到API或者SDK
|
||||
// Publish to API or SDK
|
||||
API,
|
||||
// 添加音色资源
|
||||
// Add Timbre Resources
|
||||
AddVoice,
|
||||
// 实时语音对话
|
||||
// real-time voice conversation
|
||||
RTC,
|
||||
// 导出日志
|
||||
// export log
|
||||
ExportLog,
|
||||
// 查询日志
|
||||
// query log
|
||||
FilterLog,
|
||||
}
|
||||
export function useBenefitAvailable(_props: unknown) {
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
import { UserLevel, type MemberVersionRights } from '../types';
|
||||
|
||||
interface UseBenefitBasicResult {
|
||||
name: string; // 当前用户套餐名称
|
||||
level: UserLevel; // 当前工作空间:用户的套餐级别
|
||||
compareLevel: UserLevel; // 当前工作空间:如果是专业版,值是 UserLevel.ProPersonal,其他场景同 level
|
||||
currPlan: MemberVersionRights; // 当前套餐信息
|
||||
nextPlan: MemberVersionRights; // 下一个套餐信息。如果当前已经是最高套餐级别,则值为最高级别套餐
|
||||
accountPlan: MemberVersionRights; // 账号维度的套餐信息
|
||||
instanceStatus: unknown; // 当前套餐状态,可以用来判断退订/到期状态
|
||||
isOverdue: boolean; // 是否欠费
|
||||
isExpired: boolean; // 是否过期
|
||||
isTerminated: boolean; // 是否退订
|
||||
maxMember: number; //成员上限
|
||||
name: string; // Current user package name
|
||||
level: UserLevel; // Current Workspace: User's plan level
|
||||
compareLevel: UserLevel; // Current workspace: If it is a professional version, the value is UserLevel. ProPersonal, and other scenes are at the same level.
|
||||
currPlan: MemberVersionRights; // Current package information
|
||||
nextPlan: MemberVersionRights; // Next package information. If it is already the highest package level, the value is the highest package level.
|
||||
accountPlan: MemberVersionRights; // Package information in the account dimension
|
||||
instanceStatus: unknown; // The current package status can be used to determine the unsubscribe/expiration status.
|
||||
isOverdue: boolean; // Is it in arrears?
|
||||
isExpired: boolean; // Whether it has expired
|
||||
isTerminated: boolean; // Whether to unsubscribe
|
||||
maxMember: number; //maximum membership
|
||||
}
|
||||
|
||||
const defaultData = {
|
||||
@@ -44,7 +44,7 @@ const defaultData = {
|
||||
maxMember: -1,
|
||||
};
|
||||
/**
|
||||
* 获取国内权益基础信息
|
||||
* Acquire basic information on domestic rights and interests
|
||||
*/
|
||||
export function useBenefitBasic(): UseBenefitBasicResult {
|
||||
return defaultData;
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
const quota = {
|
||||
/** 当前消耗的额度,对应到套餐内每天刷新的 */
|
||||
/** The current consumption amount corresponds to the daily refresh in the package. */
|
||||
remain: 0,
|
||||
total: 0,
|
||||
used: 0,
|
||||
/** 额外购买的额度,目前只处理国内 */
|
||||
/** The additional purchase amount is currently only processed in China. */
|
||||
extraRemain: 0,
|
||||
extraTotal: 0,
|
||||
extraUsed: 0,
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
type MemberVersionRights,
|
||||
} from '../types';
|
||||
|
||||
// 只处理最低和最高订阅服务档位
|
||||
// Only handle the lowest and highest subscription service gears
|
||||
export enum PremiumPlanLevel {
|
||||
Free = 0,
|
||||
|
||||
@@ -47,31 +47,31 @@ export interface VolcanoInfo {
|
||||
}
|
||||
|
||||
interface PremiumStoreState {
|
||||
polling: boolean; // 是否开启订阅数据自动轮询
|
||||
plans: PremiumPlan[]; // 付费订阅计划列表
|
||||
subs: PremiumSubs; // 所有订阅数据
|
||||
currentPlan: SubscriptionDetail; // 当前订阅详情
|
||||
polling: boolean; // Whether to enable automatic polling of subscription data
|
||||
plans: PremiumPlan[]; // List of paid subscription plans
|
||||
subs: PremiumSubs; // All subscription data
|
||||
currentPlan: SubscriptionDetail; // Current subscription details
|
||||
hasTrial: boolean;
|
||||
connections: BindConnection[]; // 第三方账号连接数据
|
||||
benefit: SubscriptionDetailV2; // 用户权益数据
|
||||
plansCN: Array<MemberVersionRights>; // 国内订阅套餐列表
|
||||
volcanoInfo: VolcanoInfo; // oauth跳转到火山需要用到的参数
|
||||
connections: BindConnection[]; // third-party account connection data
|
||||
benefit: SubscriptionDetailV2; // user rights data
|
||||
plansCN: Array<MemberVersionRights>; // List of domestic subscription packages
|
||||
volcanoInfo: VolcanoInfo; // Oauth jump to the volcano required parameters
|
||||
}
|
||||
|
||||
interface PremiumStoreAction {
|
||||
/**
|
||||
* 重置状态
|
||||
* reset state
|
||||
*/
|
||||
reset: () => void;
|
||||
/**
|
||||
* 设置是否轮询获取订阅数据,以下场景需要:
|
||||
* - Bot详情判断是否需要显示订阅卡片
|
||||
* - 左侧菜单栏判断是否需要显示'coze premium'
|
||||
* - 左侧菜单栏展示credits数量信息
|
||||
* To set whether to poll for subscription data, the following scenarios are required:
|
||||
* - Bot details to determine whether the subscription card needs to be displayed
|
||||
* - The left menu bar determines whether to display'coze premium'
|
||||
* - The left menu bar displays the number of credits
|
||||
*/
|
||||
setPolling: (polling: boolean) => void;
|
||||
/**
|
||||
* 获取海外订阅套餐列表
|
||||
* Get a list of overseas subscription packages
|
||||
*/
|
||||
fetchPremiumPlans: () => Promise<{
|
||||
plans: PremiumPlan[];
|
||||
@@ -79,35 +79,35 @@ interface PremiumStoreAction {
|
||||
hasTrial: boolean;
|
||||
}>;
|
||||
/**
|
||||
* 设置国内套餐列表
|
||||
* Set up a list of domestic packages
|
||||
*/
|
||||
setPremiumPlansCN: (plans: Array<MemberVersionRights>) => void;
|
||||
/**
|
||||
* 恢复订阅,暂时只有海外
|
||||
* Resume subscription, only overseas for the time being
|
||||
*/
|
||||
renewCurrentPlan: (plan: SubscriptionDetail) => void;
|
||||
/**
|
||||
* 获取当前用户订阅详情,暂时只有海外
|
||||
* Get the current user subscription details, only overseas for the time being
|
||||
*/
|
||||
fetchPremiumPlan: () => Promise<SubscriptionDetail>;
|
||||
/**
|
||||
* 取消订阅,暂时只有海外
|
||||
* Cancel the subscription, only overseas for the time being.
|
||||
*/
|
||||
cancelCurrentPlan: () => void;
|
||||
/**
|
||||
* 获取渠道绑定信息,暂时只有海外
|
||||
* Get channel binding information, only overseas for the time being.
|
||||
*/
|
||||
fetchConnections: () => Promise<void>;
|
||||
/**
|
||||
* 取消渠道用户绑定,暂时只有海外
|
||||
* Cancel the channel user binding, only overseas for the time being
|
||||
*/
|
||||
disconnectUser: (connectorId: string) => void;
|
||||
/**
|
||||
* 设置当前登录用户权益信息,海内外通用
|
||||
* Set the current logged in user rights and interests information, common at home and abroad
|
||||
*/
|
||||
setUserBenefit: (benefit: unknown) => void;
|
||||
/**
|
||||
* 设置当前账号相关火山信息
|
||||
* Set the current account related volcano information
|
||||
*/
|
||||
setVolcanoInfo: (info: VolcanoInfo) => void;
|
||||
}
|
||||
|
||||
@@ -26,22 +26,22 @@ import {
|
||||
import { type BindConnection } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
export enum UserLevel {
|
||||
/** 免费版。 */
|
||||
/** The free version. */
|
||||
Free = 0,
|
||||
/** 海外
|
||||
/** overseas
|
||||
PremiumLite */
|
||||
PremiumLite = 10,
|
||||
/** Premium */
|
||||
Premium = 15,
|
||||
PremiumPlus = 20,
|
||||
/** 国内
|
||||
V1火山专业版 */
|
||||
/** domestic
|
||||
V1 Volcano Pro */
|
||||
V1ProInstance = 100,
|
||||
/** 个人旗舰版 */
|
||||
/** Personal flagship version */
|
||||
ProPersonal = 110,
|
||||
/** 团队版 */
|
||||
/** Team Edition */
|
||||
Team = 120,
|
||||
/** 企业版 */
|
||||
/** Enterprise Edition */
|
||||
Enterprise = 130,
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const useIsPublishRecordReady = ({
|
||||
{
|
||||
manual: true,
|
||||
ready: enable,
|
||||
pollingInterval: 60 * 1000, // 60 秒轮询一次,避免过高频率请求导致服务端压力大
|
||||
pollingInterval: 60 * 1000, // Polling once every 60 seconds to avoid server level stress due to high frequency requests
|
||||
pollingErrorRetryCount: 3,
|
||||
onSuccess: target => {
|
||||
if (target) {
|
||||
|
||||
@@ -22,24 +22,24 @@ import { chatBackgroundConfig } from '../../../../../src/save-manager/auto-save/
|
||||
|
||||
describe('chatBackgroundConfig', () => {
|
||||
it('应该具有正确的配置属性', () => {
|
||||
// 验证配置的基本属性
|
||||
// Verify the basic properties of the configuration
|
||||
expect(chatBackgroundConfig).toHaveProperty('key');
|
||||
expect(chatBackgroundConfig).toHaveProperty('selector');
|
||||
expect(chatBackgroundConfig).toHaveProperty('debounce');
|
||||
expect(chatBackgroundConfig).toHaveProperty('middleware');
|
||||
|
||||
// 验证 middleware 存在且有 onBeforeSave 属性
|
||||
// Verify that middleware exists and has an onBeforeSave attribute
|
||||
expect(chatBackgroundConfig.middleware).toBeDefined();
|
||||
if (chatBackgroundConfig.middleware) {
|
||||
expect(chatBackgroundConfig.middleware).toHaveProperty('onBeforeSave');
|
||||
}
|
||||
|
||||
// 验证属性值
|
||||
// Validate attribute value
|
||||
expect(chatBackgroundConfig.key).toBe(ItemTypeExtra.ChatBackGround);
|
||||
expect(chatBackgroundConfig.debounce).toBe(DebounceTime.Immediate);
|
||||
expect(typeof chatBackgroundConfig.selector).toBe('function');
|
||||
|
||||
// 验证 onBeforeSave 是函数
|
||||
// Verify that onBeforeSave is a function
|
||||
if (
|
||||
chatBackgroundConfig.middleware &&
|
||||
chatBackgroundConfig.middleware.onBeforeSave
|
||||
@@ -51,49 +51,49 @@ describe('chatBackgroundConfig', () => {
|
||||
});
|
||||
|
||||
it('selector 应该返回 store 的 backgroundImageInfoList 属性', () => {
|
||||
// 创建模拟 store
|
||||
// Create mock store
|
||||
const mockStore = {
|
||||
backgroundImageInfoList: [
|
||||
{ id: 'bg1', url: 'http://example.com/bg1.jpg' },
|
||||
],
|
||||
};
|
||||
|
||||
// 调用 selector 函数
|
||||
// 注意:这里我们假设 selector 是一个函数,如果它是一个复杂对象,可能需要调整测试
|
||||
// Call the selector function
|
||||
// Note: Here we assume that the selector is a function, and if it is a complex object, the test may need to be adjusted
|
||||
const { selector } = chatBackgroundConfig;
|
||||
let result;
|
||||
|
||||
if (typeof selector === 'function') {
|
||||
result = selector(mockStore as any);
|
||||
// 验证结果
|
||||
// validation result
|
||||
expect(result).toBe(mockStore.backgroundImageInfoList);
|
||||
} else {
|
||||
// 如果 selector 不是函数,跳过这个测试
|
||||
// If the selector is not a function, skip this test
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('middleware.onBeforeSave 应该正确转换数据', () => {
|
||||
// 创建模拟数据
|
||||
// Create simulated data
|
||||
const mockData = [
|
||||
{ id: 'bg1', url: 'http://example.com/bg1.jpg' },
|
||||
{ id: 'bg2', url: 'http://example.com/bg2.jpg' },
|
||||
];
|
||||
|
||||
// 确保 middleware 和 onBeforeSave 存在
|
||||
// Make sure middleware and onBeforeSave exist
|
||||
if (
|
||||
chatBackgroundConfig.middleware &&
|
||||
chatBackgroundConfig.middleware.onBeforeSave
|
||||
) {
|
||||
// 调用 onBeforeSave 函数
|
||||
// Call the onBeforeSave function
|
||||
const result = chatBackgroundConfig.middleware.onBeforeSave(mockData);
|
||||
|
||||
// 验证结果
|
||||
// validation result
|
||||
expect(result).toEqual({
|
||||
background_image_info_list: mockData,
|
||||
});
|
||||
} else {
|
||||
// 如果 middleware 或 onBeforeSave 不存在,跳过这个测试
|
||||
// If middleware or onBeforeSave does not exist, skip this test
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { knowledgeConfig } from '../../../../../src/save-manager/auto-save/bot-s
|
||||
import { chatBackgroundConfig } from '../../../../../src/save-manager/auto-save/bot-skill/configs/chat-background';
|
||||
import { registers } from '../../../../../src/save-manager/auto-save/bot-skill/configs';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock(
|
||||
'../../../../../src/save-manager/auto-save/bot-skill/configs/workflows',
|
||||
() => ({
|
||||
@@ -101,7 +101,7 @@ vi.mock(
|
||||
|
||||
describe('bot-skill configs', () => {
|
||||
it('应该正确注册所有配置', () => {
|
||||
// 验证 registers 数组包含所有配置
|
||||
// Verify that the registers array contains all configurations
|
||||
expect(registers).toContain(pluginConfig);
|
||||
expect(registers).toContain(chatBackgroundConfig);
|
||||
expect(registers).toContain(onboardingConfig);
|
||||
@@ -113,7 +113,7 @@ describe('bot-skill configs', () => {
|
||||
expect(registers).toContain(workflowsConfig);
|
||||
expect(registers).toContain(voicesInfoConfig);
|
||||
|
||||
// 验证 registers 数组长度
|
||||
// Verify the length of the registers array
|
||||
expect(registers.length).toBe(10);
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('knowledgeConfig', () => {
|
||||
expect(knowledgeConfig).toHaveProperty('debounce');
|
||||
expect(knowledgeConfig).toHaveProperty('middleware');
|
||||
expect(knowledgeConfig.key).toBe(ItemType.DataSet);
|
||||
// 验证 debounce 配置
|
||||
// Verify debounce configuration
|
||||
if (typeof knowledgeConfig.debounce === 'object') {
|
||||
expect(knowledgeConfig.debounce).toHaveProperty('default');
|
||||
expect(knowledgeConfig.debounce).toHaveProperty('dataSetInfo.min_score');
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('onboardingConfig', () => {
|
||||
expect(onboardingConfig).toHaveProperty('debounce');
|
||||
expect(onboardingConfig).toHaveProperty('middleware');
|
||||
expect(onboardingConfig.key).toBe(ItemType.ONBOARDING);
|
||||
// 验证 debounce 配置
|
||||
// Verify debounce configuration
|
||||
if (typeof onboardingConfig.debounce === 'object') {
|
||||
expect(onboardingConfig.debounce).toHaveProperty('default');
|
||||
expect(onboardingConfig.debounce).toHaveProperty('prologue');
|
||||
|
||||
@@ -18,7 +18,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { botSkillSaveManager } from '../../../../src/save-manager/auto-save/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('@coze-studio/autosave', () => {
|
||||
const mockStartFn = vi.fn();
|
||||
const mockCloseFn = vi.fn();
|
||||
@@ -52,16 +52,16 @@ describe('botSkillSaveManager', () => {
|
||||
});
|
||||
|
||||
it('应该是 AutosaveManager 的实例', () => {
|
||||
// 验证 botSkillSaveManager 是 AutosaveManager 的实例
|
||||
// Verify that botSkillSaveManager is an instance of AutosaveManager
|
||||
expect(botSkillSaveManager).toBeDefined();
|
||||
// 由于我们模拟了 AutosaveManager,我们不能直接检查实例类型
|
||||
// 但可以检查它是否具有 AutosaveManager 实例应有的属性和方法
|
||||
// Since we simulate AutosaveManager, we cannot directly check the instance type
|
||||
// But you can check if it has the properties and methods that an AutosaveManager instance should have
|
||||
expect(botSkillSaveManager).toHaveProperty('start');
|
||||
expect(botSkillSaveManager).toHaveProperty('close');
|
||||
});
|
||||
|
||||
it('应该具有 start 和 close 方法', () => {
|
||||
// 验证 botSkillSaveManager 具有 start 和 close 方法
|
||||
// Verify botSkillSaveManager has a start and close method
|
||||
expect(botSkillSaveManager.start).toBeDefined();
|
||||
expect(botSkillSaveManager.close).toBeDefined();
|
||||
expect(typeof botSkillSaveManager.start).toBe('function');
|
||||
@@ -69,16 +69,16 @@ describe('botSkillSaveManager', () => {
|
||||
});
|
||||
|
||||
it('调用 start 方法应该正常工作', () => {
|
||||
// 调用 start 方法
|
||||
// Call the start method
|
||||
botSkillSaveManager.start();
|
||||
// 由于我们已经模拟了 start 方法,这里只需验证它可以被调用而不会抛出错误
|
||||
// Since we have already simulated the start method, we just need to verify that it can be called without throwing an error
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('调用 close 方法应该正常工作', () => {
|
||||
// 调用 close 方法
|
||||
// Call the close method
|
||||
botSkillSaveManager.close();
|
||||
// 由于我们已经模拟了 close 方法,这里只需验证它可以被调用而不会抛出错误
|
||||
// Since we have simulated the close method, we just need to verify that it can be called without throwing an error
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import { modelSaveManager } from '../../../src/save-manager/auto-save/model';
|
||||
import { autosaveManager } from '../../../src/save-manager/auto-save/index';
|
||||
import { botSkillSaveManager } from '../../../src/save-manager/auto-save/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/save-manager/auto-save/persona', () => ({
|
||||
personaSaveManager: {
|
||||
start: vi.fn(),
|
||||
@@ -46,24 +46,24 @@ vi.mock('../../../src/save-manager/auto-save/bot-skill', () => ({
|
||||
describe('autosave manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// 正确模拟 console.log
|
||||
// Correctly emulate console.log
|
||||
vi.spyOn(console, 'log').mockImplementation(() => {
|
||||
// 什么都不做
|
||||
// Do nothing.
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// 恢复原始的 console.log
|
||||
// Restore the original console.log
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('应该在启动时调用所有管理器的 start 方法', () => {
|
||||
autosaveManager.start();
|
||||
|
||||
// 验证 console.log 被调用
|
||||
// Verify that console.log is called
|
||||
expect(console.log).toHaveBeenCalledWith('start:>>');
|
||||
|
||||
// 验证所有管理器的 start 方法被调用
|
||||
// Verify that the start method of all managers is called
|
||||
expect(personaSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
expect(botSkillSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
expect(modelSaveManager.start).toHaveBeenCalledTimes(1);
|
||||
@@ -72,10 +72,10 @@ describe('autosave manager', () => {
|
||||
it('应该在关闭时调用所有管理器的 close 方法', () => {
|
||||
autosaveManager.close();
|
||||
|
||||
// 验证 console.log 被调用
|
||||
// Verify that console.log is called
|
||||
expect(console.log).toHaveBeenCalledWith('close:>>');
|
||||
|
||||
// 验证所有管理器的 close 方法被调用
|
||||
// Verify that all managers' close methods are called
|
||||
expect(personaSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
expect(botSkillSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
expect(modelSaveManager.close).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import { saveFetcher } from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveRequest } from '../../../src/save-manager/auto-save/request';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
UpdateDraftBotInfoAgw: vi.fn(),
|
||||
@@ -53,7 +53,7 @@ describe('auto-save request', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
botId: mockBotId,
|
||||
});
|
||||
@@ -71,19 +71,19 @@ describe('auto-save request', () => {
|
||||
it('应该使用正确的参数调用 saveFetcher', async () => {
|
||||
await saveRequest(mockPayload, mockItemType);
|
||||
|
||||
// 验证 saveFetcher 被调用
|
||||
// Verify that saveFetcher is called
|
||||
expect(saveFetcher).toHaveBeenCalledTimes(1);
|
||||
// 验证 saveFetcher 的第二个参数是正确的 itemType
|
||||
// Verify that the second argument of saveFetcher is the correct itemType.
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
mockItemType,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
// Get and execute the first argument (function) of saveFetcher.
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateDraftBotInfoAgw 被调用,并且参数正确
|
||||
// Verify that UpdateDraftBotInfoAgw is called and the parameters are correct
|
||||
expect(PlaygroundApi.UpdateDraftBotInfoAgw).toHaveBeenCalledWith({
|
||||
bot_info: {
|
||||
bot_id: mockBotId,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveDevHooksConfig } from '../../../src/save-manager/manual-save/dev-hooks';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -45,7 +45,7 @@ describe('dev-hooks save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
devHooks: mockDevHooks,
|
||||
});
|
||||
@@ -66,12 +66,12 @@ describe('dev-hooks save manager', () => {
|
||||
} as any as HookInfo;
|
||||
await saveDevHooksConfig(newConfig);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
hook_info: newConfig,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemType.HOOKINFO,
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
import { saveTableMemory } from '../../../src/save-manager/manual-save/memory-table';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -49,7 +49,7 @@ describe('memory-table save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
databaseList: mockDatabaseList,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
@@ -68,17 +68,17 @@ describe('memory-table save manager', () => {
|
||||
it('应该正确保存内存表变量', async () => {
|
||||
await saveTableMemory();
|
||||
|
||||
// 验证 transformVo2Dto.databaseList 被调用
|
||||
// Verify that transformVo2Dto.DatabaseList is called
|
||||
expect(mockTransformVo2Dto.databaseList).toHaveBeenCalledWith(
|
||||
mockDatabaseList,
|
||||
);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
database_list: { transformed: mockDatabaseList },
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemType.TABLE,
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
saveConnectorType,
|
||||
} from '../../../src/save-manager/manual-save/multi-agent';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
PlaygroundApi: {
|
||||
UpdateAgentV2: vi.fn(),
|
||||
@@ -68,7 +68,7 @@ vi.mock('../../../src/save-manager/utils/save-fetcher', () => ({
|
||||
describe('multi-agent save manager', () => {
|
||||
const mockBotId = 'mock-bot-id';
|
||||
const mockSpaceId = 'mock-space-id';
|
||||
// 创建一个符合 Agent 类型的模拟对象
|
||||
// Create a mock object that conforms to the Agent type
|
||||
const mockAgent = {
|
||||
id: 'agent-1',
|
||||
name: 'Agent 1',
|
||||
@@ -99,7 +99,7 @@ describe('multi-agent save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
botId: mockBotId,
|
||||
});
|
||||
@@ -133,22 +133,22 @@ describe('multi-agent save manager', () => {
|
||||
it('应该正确更新代理', async () => {
|
||||
await saveUpdateAgents(mockAgent as any);
|
||||
|
||||
// 验证 transformVo2Dto.agent 被调用
|
||||
// Verify that transformVo2Dato.agent is called
|
||||
expect(
|
||||
useMultiAgentStore.getState().transformVo2Dto.agent,
|
||||
).toHaveBeenCalledWith(mockAgent);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
// Get and execute the first argument (function) of saveFetcher.
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateAgentV2 被调用,并且参数正确
|
||||
// Verify that UpdateAgentV2 is called and the parameters are correct
|
||||
expect(PlaygroundApi.UpdateAgentV2).toHaveBeenCalledWith({
|
||||
...mockAgentDto,
|
||||
bot_id: mockBotId,
|
||||
@@ -163,17 +163,17 @@ describe('multi-agent save manager', () => {
|
||||
const agentId = 'agent-to-delete';
|
||||
await saveDeleteAgents(agentId);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
// Get and execute the first argument (function) of saveFetcher.
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateAgentV2 被调用,并且参数正确
|
||||
// Verify that UpdateAgentV2 is called and the parameters are correct
|
||||
expect(PlaygroundApi.UpdateAgentV2).toHaveBeenCalledWith({
|
||||
bot_id: mockBotId,
|
||||
space_id: mockSpaceId,
|
||||
@@ -188,17 +188,17 @@ describe('multi-agent save manager', () => {
|
||||
it('应该正确保存多代理数据', async () => {
|
||||
await saveMultiAgentData();
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.MultiAgent,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
// Get and execute the first argument (function) of saveFetcher.
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateMultiAgent 被调用,并且参数正确
|
||||
// Verify that UpdateMultiAgent is invoked and the parameters are correct
|
||||
expect(PlaygroundApi.UpdateMultiAgent).toHaveBeenCalledWith({
|
||||
space_id: mockSpaceId,
|
||||
bot_id: mockBotId,
|
||||
@@ -210,21 +210,21 @@ describe('multi-agent save manager', () => {
|
||||
|
||||
describe('saveConnectorType', () => {
|
||||
it('应该正确保存连接器类型', async () => {
|
||||
// 使用数字代替枚举值
|
||||
const connectorType = 0; // 假设 0 代表 Straight
|
||||
// Using numbers instead of enumerated values
|
||||
const connectorType = 0; // Assume 0 is Straight
|
||||
await saveConnectorType(connectorType as any);
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.ConnectorType,
|
||||
);
|
||||
|
||||
// 获取并执行 saveFetcher 的第一个参数(函数)
|
||||
// Get and execute the first argument (function) of saveFetcher.
|
||||
const saveRequestFn = (saveFetcher as any).mock.calls[0][0];
|
||||
await saveRequestFn();
|
||||
|
||||
// 验证 UpdateMultiAgent 被调用,并且参数正确
|
||||
// Verify that UpdateMultiAgent is invoked and the parameters are correct
|
||||
expect(PlaygroundApi.UpdateMultiAgent).toHaveBeenCalledWith({
|
||||
space_id: mockSpaceId,
|
||||
bot_id: mockBotId,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { updateQueryCollect } from '../../../src/save-manager/manual-save/query-collect';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/store/query-collect', () => ({
|
||||
useQueryCollectStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -46,7 +46,7 @@ describe('query-collect save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useQueryCollectStore.getState as any).mockReturnValue({
|
||||
...mockQueryCollect,
|
||||
});
|
||||
@@ -62,18 +62,18 @@ describe('query-collect save manager', () => {
|
||||
});
|
||||
|
||||
it('应该正确保存 query collect 配置', async () => {
|
||||
// 创建一个符合 UserQueryCollectConf 类型的对象作为参数
|
||||
// Create an object of UserQueryCollectConf type as a parameter
|
||||
const queryCollectConf =
|
||||
mockQueryCollect as unknown as UserQueryCollectConf;
|
||||
|
||||
await updateQueryCollect(queryCollectConf);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
user_query_collect_conf: queryCollectConf,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.QueryCollect,
|
||||
@@ -84,7 +84,7 @@ describe('query-collect save manager', () => {
|
||||
const mockError = new Error('Save failed');
|
||||
(saveFetcher as any).mockRejectedValue(mockError);
|
||||
|
||||
// 创建一个符合 UserQueryCollectConf 类型的对象作为参数
|
||||
// Create an object of UserQueryCollectConf type as a parameter
|
||||
const queryCollectConf =
|
||||
mockQueryCollect as unknown as UserQueryCollectConf;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { updateShortcutSort } from '../../../src/save-manager/manual-save/shortcuts';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -46,7 +46,7 @@ describe('shortcuts save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
shortcut: mockShortcut,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
@@ -66,12 +66,12 @@ describe('shortcuts save manager', () => {
|
||||
const newSort = ['shortcut-2', 'shortcut-1'];
|
||||
await updateShortcutSort(newSort);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
shortcut_sort: newSort,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.Shortcut,
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { saveTimeCapsule } from '../../../src/save-manager/manual-save/time-capsule';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -54,7 +54,7 @@ describe('time-capsule save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
timeCapsule: mockTimeCapsule,
|
||||
transformVo2Dto: mockTransformVo2Dto,
|
||||
@@ -73,17 +73,17 @@ describe('time-capsule save manager', () => {
|
||||
it('应该正确保存 time capsule 配置', async () => {
|
||||
await saveTimeCapsule();
|
||||
|
||||
// 验证 transformVo2Dto.timeCapsule 被调用,参数应该是包含 time_capsule_mode 和 disable_prompt_calling 的对象
|
||||
// Verify that transformVo2To.timeCapsule is called, the argument should be an object containing time_capsule_mode and disable_prompt_calling
|
||||
expect(mockTransformVo2Dto.timeCapsule).toHaveBeenCalledWith({
|
||||
time_capsule_mode: mockTimeCapsule.time_capsule_mode,
|
||||
disable_prompt_calling: mockTimeCapsule.disable_prompt_calling,
|
||||
});
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
bot_tag_info: mockTransformedTimeCapsule,
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.TimeCapsule,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { ItemTypeExtra } from '../../../src/save-manager/types';
|
||||
import { saveTTSConfig } from '../../../src/save-manager/manual-save/tts';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('lodash-es', () => ({
|
||||
cloneDeep: vi.fn(obj => JSON.parse(JSON.stringify(obj))),
|
||||
merge: vi.fn((target, ...sources) => Object.assign({}, target, ...sources)),
|
||||
@@ -73,7 +73,7 @@ describe('tts save manager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotSkillStore.getState as any).mockReturnValue({
|
||||
tts: mockTTS,
|
||||
voicesInfo: mockVoicesInfo,
|
||||
@@ -93,21 +93,21 @@ describe('tts save manager', () => {
|
||||
it('应该正确保存 TTS 配置', async () => {
|
||||
await saveTTSConfig();
|
||||
|
||||
// 验证 transformVo2Dto.tts 被调用
|
||||
// Verify that transformVo2To.tts is called
|
||||
expect(mockTransformVo2Dto.tts).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证传递给 transformVo2Dto.tts 的参数是 tts 的克隆
|
||||
// Verify that the parameter passed to transformVo2To.tts is a clone of tts
|
||||
const ttsArg = mockTransformVo2Dto.tts.mock.calls[0][0];
|
||||
expect(ttsArg).toEqual(mockTTS);
|
||||
expect(ttsArg).not.toBe(mockTTS); // 确保是克隆而不是原始对象
|
||||
expect(ttsArg).not.toBe(mockTTS); // Make sure it's a clone and not the original object
|
||||
|
||||
// 验证 cloneDeep 被调用
|
||||
// Verify that cloneDeep is called
|
||||
expect(cloneDeep).toHaveBeenCalledTimes(3);
|
||||
|
||||
// 验证 transformVo2Dto.voicesInfo 被调用
|
||||
// Verify that transformVo2To.voicesInfo is called
|
||||
expect(mockTransformVo2Dto.voicesInfo).toHaveBeenCalledWith(mockVoicesInfo);
|
||||
|
||||
// 验证 updateBotRequest 被调用,并且参数正确
|
||||
// Verify that updateBotRequest was called and the parameters are correct
|
||||
expect(updateBotRequest).toHaveBeenCalledWith({
|
||||
voices_info: {
|
||||
muted: mockTTS.muted,
|
||||
@@ -120,7 +120,7 @@ describe('tts save manager', () => {
|
||||
},
|
||||
});
|
||||
|
||||
// 验证 saveFetcher 被调用,并且参数正确
|
||||
// Verify that saveFetcher is called and the parameters are correct
|
||||
expect(saveFetcher).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
ItemTypeExtra.TTS,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { useBotSkillStore } from '../../../src/store/bot-skill';
|
||||
import { useBotInfoStore } from '../../../src/store/bot-info';
|
||||
import { getBotDetailDtoInfo } from '../../../src/save-manager/utils/bot-dto-info';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('@coze-arch/report-events', () => ({
|
||||
REPORT_EVENTS: {
|
||||
botDebugSaveAll: 'botDebugSaveAll',
|
||||
@@ -130,7 +130,7 @@ describe('bot-dto-info utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
mode: BotMode.SingleMode,
|
||||
});
|
||||
@@ -156,18 +156,18 @@ describe('bot-dto-info utils', () => {
|
||||
it('应该正确转换所有 bot 信息为 DTO 格式', () => {
|
||||
const result = getBotDetailDtoInfo();
|
||||
|
||||
// 验证 bot skill info
|
||||
// Verify bot skill info
|
||||
const { botSkillInfo } = result;
|
||||
|
||||
// 验证 persona 转换
|
||||
// Validate persona conversion
|
||||
expect(mockPersona.transformVo2Dto).toHaveBeenCalledWith(
|
||||
mockPersona.systemMessage,
|
||||
);
|
||||
|
||||
// 验证 model 转换
|
||||
// Validation model transformation
|
||||
expect(mockModel.transformVo2Dto).toHaveBeenCalledWith(mockModel.config);
|
||||
|
||||
// 验证 bot skill 转换
|
||||
// Verify bot skill conversion
|
||||
expect(mockTransformVo2Dto.knowledge).toHaveBeenCalledWith(
|
||||
mockBotSkill.knowledge,
|
||||
);
|
||||
@@ -203,17 +203,17 @@ describe('bot-dto-info utils', () => {
|
||||
mockBotSkill.voicesInfo,
|
||||
);
|
||||
|
||||
// 验证 queryCollect 转换
|
||||
// Verify queryCollect conversion
|
||||
expect(mockQueryCollect.transformVo2Dto).toHaveBeenCalledWith(
|
||||
mockQueryCollect,
|
||||
);
|
||||
|
||||
// 验证结果结构
|
||||
// Validation result structure
|
||||
expect(botSkillInfo).toBeDefined();
|
||||
});
|
||||
|
||||
it('在多智能体模式下应该正确转换', () => {
|
||||
// 设置为多智能体模式
|
||||
// Set to multi-agent mode
|
||||
(useBotInfoStore.getState as any).mockReturnValue({
|
||||
mode: BotMode.MultiMode,
|
||||
});
|
||||
@@ -221,12 +221,12 @@ describe('bot-dto-info utils', () => {
|
||||
const result = getBotDetailDtoInfo();
|
||||
const { botSkillInfo } = result;
|
||||
|
||||
// 验证多智能体模式下的转换
|
||||
// Verify transitions in multi-agent mode
|
||||
expect(mockMultiAgent.transformVo2Dto.agent).toHaveBeenCalledWith(
|
||||
mockMultiAgent.agents[0],
|
||||
);
|
||||
|
||||
// 验证多智能体模式下某些字段应该是 undefined
|
||||
// Verify that some fields should be undefined in multi-agent mode
|
||||
expect(botSkillInfo).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
updateBotRequest,
|
||||
} from '../../../src/save-manager/utils/save-fetcher';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
reporter: {
|
||||
successEvent: vi.fn(),
|
||||
@@ -84,7 +84,7 @@ describe('save-fetcher utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// 设置默认状态
|
||||
// Set default state
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
@@ -118,7 +118,7 @@ describe('save-fetcher utils', () => {
|
||||
|
||||
describe('saveFetcher', () => {
|
||||
it('应该在只读模式下不执行任何操作', async () => {
|
||||
// 设置为只读模式
|
||||
// Set to read-only mode
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: false,
|
||||
isPreview: false,
|
||||
@@ -134,7 +134,7 @@ describe('save-fetcher utils', () => {
|
||||
});
|
||||
|
||||
it('应该在预览模式下不执行任何操作', async () => {
|
||||
// 设置为预览模式
|
||||
// Set to preview mode
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: true,
|
||||
@@ -150,7 +150,7 @@ describe('save-fetcher utils', () => {
|
||||
});
|
||||
|
||||
it('应该在探索模式下不执行任何操作', async () => {
|
||||
// 设置为探索模式
|
||||
// Set to exploration mode
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
@@ -166,7 +166,7 @@ describe('save-fetcher utils', () => {
|
||||
});
|
||||
|
||||
it('应该在未初始化时不执行任何操作', async () => {
|
||||
// 设置为未初始化
|
||||
// Set to uninitialized
|
||||
(usePageRuntimeStore.getState as any).mockReturnValue({
|
||||
editable: true,
|
||||
isPreview: false,
|
||||
@@ -184,32 +184,32 @@ describe('save-fetcher utils', () => {
|
||||
it('应该在可编辑模式下正确执行保存操作', async () => {
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置保存状态
|
||||
// Verify settings save state
|
||||
expect(mockSetPageRuntimeByImmer).toHaveBeenCalledTimes(3);
|
||||
// 验证第一次调用 - 设置保存中状态
|
||||
// Verify First Call - Set Saved State
|
||||
const firstCall = mockSetPageRuntimeByImmer.mock.calls[0][0];
|
||||
const mockState1 = { savingInfo: {} };
|
||||
firstCall(mockState1);
|
||||
expect(mockState1.savingInfo.saving).toBe(true);
|
||||
expect(mockState1.savingInfo.scopeKey).toBe(String(mockScopeKey));
|
||||
|
||||
// 验证保存请求被调用
|
||||
// Verify that the save request was invoked
|
||||
expect(mockSaveRequest).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证第二次调用 - 设置保存完成状态
|
||||
// Verify Second Call - Set Save Complete Status
|
||||
const secondCall = mockSetPageRuntimeByImmer.mock.calls[1][0];
|
||||
const mockState2 = { savingInfo: {} };
|
||||
secondCall(mockState2);
|
||||
expect(mockState2.savingInfo.saving).toBe(false);
|
||||
expect(mockState2.savingInfo.time).toBe('12:34:56');
|
||||
|
||||
// 验证第三次调用 - 设置未发布变更状态
|
||||
// Verify Third Call - Set Unpublished Change Status
|
||||
const thirdCall = mockSetPageRuntimeByImmer.mock.calls[2][0];
|
||||
const mockState3 = {};
|
||||
thirdCall(mockState3);
|
||||
expect(mockState3.hasUnpublishChange).toBe(true);
|
||||
|
||||
// 验证设置协作状态
|
||||
// Verify settings collaboration status
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalledTimes(1);
|
||||
const collaborationCall = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockCollabState = { branch: { id: 'branch-id' } };
|
||||
@@ -217,7 +217,7 @@ describe('save-fetcher utils', () => {
|
||||
expect(mockCollabState.sameWithOnline).toBe(false);
|
||||
expect(mockCollabState.branch).toEqual({ id: 'updated-branch-id' });
|
||||
|
||||
// 验证成功事件被报告
|
||||
// Validation success events are reported
|
||||
expect(reporter.successEvent).toHaveBeenCalledWith({
|
||||
eventName: REPORT_EVENTS.AutosaveSuccess,
|
||||
meta: { itemType: mockScopeKey },
|
||||
@@ -230,13 +230,13 @@ describe('save-fetcher utils', () => {
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置保存中状态
|
||||
// Verify settings save status
|
||||
expect(mockSetPageRuntimeByImmer).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证保存请求被调用
|
||||
// Verify that the save request was invoked
|
||||
expect(mockSaveRequest).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 验证错误事件被报告
|
||||
// Validation error events are reported
|
||||
expect(reporter.errorEvent).toHaveBeenCalledWith({
|
||||
eventName: REPORT_EVENTS.AutosaveError,
|
||||
error: mockError,
|
||||
@@ -249,19 +249,19 @@ describe('save-fetcher utils', () => {
|
||||
data: {
|
||||
has_change: true,
|
||||
same_with_online: false,
|
||||
// 没有 branch 信息
|
||||
// No branch information
|
||||
},
|
||||
});
|
||||
|
||||
await saveFetcher(mockSaveRequest, mockScopeKey as any);
|
||||
|
||||
// 验证设置协作状态
|
||||
// Verify settings collaboration status
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalledTimes(1);
|
||||
const collaborationCall = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockCollabState = { branch: { id: 'branch-id' } };
|
||||
collaborationCall(mockCollabState);
|
||||
expect(mockCollabState.sameWithOnline).toBe(false);
|
||||
// 分支信息应该保持不变
|
||||
// Branch information should remain unchanged
|
||||
expect(mockCollabState.branch).toEqual({ id: 'branch-id' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
|
||||
describe('diff-task store', () => {
|
||||
beforeEach(() => {
|
||||
// 每个测试前重置 store 状态
|
||||
// Reset the stored state before each test
|
||||
useDiffTaskStore.getState().clear();
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('diff-task store', () => {
|
||||
|
||||
const state = useDiffTaskStore.getState();
|
||||
expect(state.diffTask).toBe('model');
|
||||
// promptDiffInfo 应该保持不变
|
||||
// promptDiffInfo should remain unchanged
|
||||
expect(state.promptDiffInfo).toEqual(
|
||||
getDefaultDiffTaskStore().promptDiffInfo,
|
||||
);
|
||||
@@ -108,43 +108,43 @@ describe('diff-task store', () => {
|
||||
test('exitDiffMode 应该调用 clear 方法', () => {
|
||||
const { enterDiffMode, exitDiffMode, clear } = useDiffTaskStore.getState();
|
||||
|
||||
// 模拟 clear 方法
|
||||
// Simulated clearing method
|
||||
const mockClear = vi.fn();
|
||||
useDiffTaskStore.setState(state => ({ ...state, clear: mockClear }));
|
||||
|
||||
// 先进入 diff 模式
|
||||
// Enter diff mode first
|
||||
enterDiffMode({ diffTask: 'prompt' });
|
||||
|
||||
// 退出 diff 模式
|
||||
// Exit diff mode
|
||||
exitDiffMode();
|
||||
|
||||
// 验证 clear 被调用
|
||||
// Verify clear is called
|
||||
expect(mockClear).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 恢复原始的 clear 方法
|
||||
// Restore the original clear method
|
||||
useDiffTaskStore.setState(state => ({ ...state, clear }));
|
||||
});
|
||||
|
||||
test('clear 应该重置状态到默认值', () => {
|
||||
const { setDiffTask, clear } = useDiffTaskStore.getState();
|
||||
|
||||
// 修改状态
|
||||
// Modify state
|
||||
setDiffTask({
|
||||
diffTask: 'model',
|
||||
hasContinueTask: true,
|
||||
continueTask: 'prompt',
|
||||
});
|
||||
|
||||
// 验证状态已更改
|
||||
// Verification status has changed
|
||||
let state = useDiffTaskStore.getState();
|
||||
expect(state.diffTask).toBe('model');
|
||||
expect(state.hasContinueTask).toBe(true);
|
||||
expect(state.continueTask).toBe('prompt');
|
||||
|
||||
// 重置状态
|
||||
// reset state
|
||||
clear();
|
||||
|
||||
// 验证状态已重置
|
||||
// Verification status reset
|
||||
state = useDiffTaskStore.getState();
|
||||
expect(state).toEqual({
|
||||
...getDefaultDiffTaskStore(),
|
||||
|
||||
@@ -665,9 +665,9 @@ describe('resetHostAgent', () => {
|
||||
});
|
||||
|
||||
useBotInfoStore.getState().setBotInfo(getOverall());
|
||||
// 接口错误返回
|
||||
// interface error return
|
||||
expect(await useMultiAgentStore.getState().addAgent({})).toBeUndefined();
|
||||
// 接口错误返回走default
|
||||
// Interface error return go default
|
||||
expect(useCollaborationStore.getState().sameWithOnline).toEqual(
|
||||
getDefaultCollaborationStore().sameWithOnline,
|
||||
);
|
||||
@@ -1051,7 +1051,7 @@ describe('addAgentIntent', () => {
|
||||
};
|
||||
useMultiAgentStore.getState().addAgent2Store(agent);
|
||||
const findAgent = useMultiAgentStore.getState().agents?.[0];
|
||||
// 这里想要mock findTargetAgent的返回值
|
||||
// Here I want to mock the return value of findTargetAgent
|
||||
const mockFindAgent = vi
|
||||
.spyOn(findAgentModule, 'findTargetAgent')
|
||||
.mockReturnValueOnce({
|
||||
|
||||
@@ -19,7 +19,7 @@ import { globalVars } from '@coze-arch/web-context';
|
||||
|
||||
import { getExecuteDraftBotRequestId } from '../../src/utils/execute-draft-bot-request-id';
|
||||
|
||||
// 模拟 globalVars
|
||||
// Simulate globalVars
|
||||
vi.mock('@coze-arch/web-context', () => ({
|
||||
globalVars: {
|
||||
LAST_EXECUTE_ID: 'mock-execute-id',
|
||||
@@ -35,14 +35,14 @@ describe('execute-draft-bot-request-id utils', () => {
|
||||
});
|
||||
|
||||
it('应该在 LAST_EXECUTE_ID 变化时返回新值', () => {
|
||||
// 修改模拟的 LAST_EXECUTE_ID
|
||||
// Modify the simulated LAST_EXECUTE_ID
|
||||
(globalVars as any).LAST_EXECUTE_ID = 'new-execute-id';
|
||||
|
||||
const result = getExecuteDraftBotRequestId();
|
||||
|
||||
expect(result).toBe('new-execute-id');
|
||||
|
||||
// 恢复原始值,避免影响其他测试
|
||||
// Restore the original value to avoid affecting other tests
|
||||
(globalVars as any).LAST_EXECUTE_ID = 'mock-execute-id';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
} from '../../src/types/generate-image';
|
||||
import { useBotSkillStore } from '../../src/store/bot-skill';
|
||||
|
||||
// 模拟依赖
|
||||
// simulated dependency
|
||||
vi.mock('../../src/store/bot-skill', () => ({
|
||||
useBotSkillStore: {
|
||||
getState: vi.fn(),
|
||||
@@ -124,8 +124,8 @@ describe('generate-image utils', () => {
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Generating) // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.None); // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.Generating) // static graph state
|
||||
.mockReturnValueOnce(DotStatus.None); // animation status
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
@@ -173,8 +173,8 @@ describe('generate-image utils', () => {
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.None) // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.None) // static graph state
|
||||
.mockReturnValueOnce(DotStatus.Success); // animation status
|
||||
|
||||
getInitBackgroundInfo(data, state);
|
||||
|
||||
@@ -276,13 +276,13 @@ describe('generate-image utils', () => {
|
||||
|
||||
(getDotStatus as any).mockReturnValue(DotStatus.None);
|
||||
|
||||
// 在调用函数前,先准备一个空的任务对象,模拟函数内部的行为
|
||||
// Before calling the function, prepare an empty task object to simulate the behavior inside the function
|
||||
const emptyTask = {
|
||||
id: '',
|
||||
img_info: {},
|
||||
};
|
||||
|
||||
// 修改测试数据,添加一个空任务
|
||||
// Modify the test data and add an empty task
|
||||
data.tasks = [emptyTask as any];
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
@@ -291,9 +291,9 @@ describe('generate-image utils', () => {
|
||||
expect(state.gif.loading).toBe(false);
|
||||
expect(state.image.loading).toBe(false);
|
||||
|
||||
// 直接修改 state.selectedImage,使其与预期值匹配
|
||||
// Modify state.selectedImage directly to match the expected value
|
||||
state.selectedImage = emptyTask;
|
||||
// 修改断言,与实际函数行为一致
|
||||
// Modify the assertion to match the actual function behavior
|
||||
expect(state.selectedImage).toEqual(emptyTask);
|
||||
});
|
||||
|
||||
@@ -340,8 +340,8 @@ describe('generate-image utils', () => {
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.None) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.None) // animation status
|
||||
.mockReturnValueOnce(DotStatus.Success); // static graph state
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
@@ -398,8 +398,8 @@ describe('generate-image utils', () => {
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Generating) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.None); // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.Generating) // animation status
|
||||
.mockReturnValueOnce(DotStatus.None); // static graph state
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
@@ -473,8 +473,8 @@ describe('generate-image utils', () => {
|
||||
};
|
||||
|
||||
(getDotStatus as any)
|
||||
.mockReturnValueOnce(DotStatus.Success) // 动图状态
|
||||
.mockReturnValueOnce(DotStatus.Success); // 静态图状态
|
||||
.mockReturnValueOnce(DotStatus.Success) // animation status
|
||||
.mockReturnValueOnce(DotStatus.Success); // static graph state
|
||||
|
||||
getInitAvatarInfo(data, state);
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { DotStatus } from '../../src/types/generate-image';
|
||||
|
||||
// 模拟 PicType 枚举
|
||||
// Analog PicType Enumeration
|
||||
enum MockPicType {
|
||||
AVATAR = 1,
|
||||
BACKGROUND = 2,
|
||||
}
|
||||
|
||||
// 模拟 GetPicTaskData 类型
|
||||
// Emulate the GetPicTaskData type
|
||||
interface MockTask {
|
||||
type: MockPicType;
|
||||
status: number;
|
||||
@@ -40,7 +40,7 @@ interface MockGetPicTaskData {
|
||||
notices?: MockNotice[];
|
||||
}
|
||||
|
||||
// 简化版的 getDotStatus 函数
|
||||
// Simplified version of getDotStatus function
|
||||
function simplifiedGetDotStatus(
|
||||
data: MockGetPicTaskData | null,
|
||||
picType: MockPicType,
|
||||
|
||||
@@ -20,7 +20,7 @@ import { type Branch, type Committer } from '@coze-arch/bot-api/developer_api';
|
||||
import { updateHeaderStatus } from '../../src/utils/handle-status';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
|
||||
// 模拟 useCollaborationStore
|
||||
// Analog useCollaborationStore
|
||||
vi.mock('../../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
@@ -59,7 +59,7 @@ describe('handle-status utils', () => {
|
||||
expect(useCollaborationStore.getState).toHaveBeenCalled();
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalled();
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
// Validate setCollaborationByImmer callback function
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: false,
|
||||
@@ -91,7 +91,7 @@ describe('handle-status utils', () => {
|
||||
setCollaborationByImmer: mockSetCollaborationByImmer,
|
||||
});
|
||||
|
||||
// 只提供部分参数
|
||||
// Only some parameters are provided
|
||||
const mockProps = {
|
||||
same_with_online: true,
|
||||
};
|
||||
@@ -101,7 +101,7 @@ describe('handle-status utils', () => {
|
||||
expect(useCollaborationStore.getState).toHaveBeenCalled();
|
||||
expect(mockSetCollaborationByImmer).toHaveBeenCalled();
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
// Validate setCollaborationByImmer callback function
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: false,
|
||||
@@ -114,7 +114,7 @@ describe('handle-status utils', () => {
|
||||
|
||||
callback(mockStore);
|
||||
|
||||
// 只有 sameWithOnline 应该被更新
|
||||
// Only sameWithOnline should be updated
|
||||
expect(mockStore).toEqual({
|
||||
sameWithOnline: true,
|
||||
commit_time: 'old_time',
|
||||
@@ -133,13 +133,13 @@ describe('handle-status utils', () => {
|
||||
|
||||
const mockProps = {
|
||||
committer: {
|
||||
// commit_time 和 name 都是 undefined
|
||||
// commit_time and name are both undefined.
|
||||
} as Committer,
|
||||
};
|
||||
|
||||
updateHeaderStatus(mockProps);
|
||||
|
||||
// 验证 setCollaborationByImmer 的回调函数
|
||||
// Validate setCollaborationByImmer callback function
|
||||
const callback = mockSetCollaborationByImmer.mock.calls[0][0];
|
||||
const mockStore = {
|
||||
sameWithOnline: true,
|
||||
@@ -149,7 +149,7 @@ describe('handle-status utils', () => {
|
||||
|
||||
callback(mockStore);
|
||||
|
||||
// 应该使用空字符串作为默认值
|
||||
// Should use empty string as default
|
||||
expect(mockStore).toEqual({
|
||||
sameWithOnline: false,
|
||||
commit_time: '',
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
describe('plugin-apis', () => {
|
||||
describe('getPluginApisFilterExample', () => {
|
||||
it('应该过滤掉所有插件API中的debug_example字段', () => {
|
||||
// 使用 as unknown as PluginApi[] 来绕过类型检查
|
||||
// Use as unknown as PluginApi [] to bypass type checking
|
||||
const mockPluginApis = [
|
||||
{
|
||||
name: 'plugin1',
|
||||
@@ -56,7 +56,7 @@ describe('plugin-apis', () => {
|
||||
|
||||
describe('getSinglePluginApiFilterExample', () => {
|
||||
it('应该过滤掉单个插件API中的debug_example字段', () => {
|
||||
// 使用 as unknown as PluginApi 来绕过类型检查
|
||||
// Use as unknown as PluginApi to bypass type checking
|
||||
const mockPluginApi = {
|
||||
name: 'plugin1',
|
||||
debug_example: 'example1',
|
||||
|
||||
@@ -30,20 +30,20 @@ describe('replacedBotPrompt', () => {
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
// Check system prompt
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '这是一个系统提示',
|
||||
record_id: '123456',
|
||||
});
|
||||
|
||||
// 检查用户前缀
|
||||
// Check user prefix
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 检查用户后缀
|
||||
// Check user suffix
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
@@ -60,20 +60,20 @@ describe('replacedBotPrompt', () => {
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
// Check system prompt
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '',
|
||||
record_id: '',
|
||||
});
|
||||
|
||||
// 检查用户前缀
|
||||
// Check user prefix
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 检查用户后缀
|
||||
// Check user suffix
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
@@ -89,7 +89,7 @@ describe('replacedBotPrompt', () => {
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 检查系统提示
|
||||
// Check system prompt
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '这是一个系统提示',
|
||||
|
||||
@@ -20,7 +20,7 @@ import { PromptType } from '@coze-arch/bot-api/developer_api';
|
||||
import { getReplacedBotPrompt } from '../../src/utils/save';
|
||||
import { usePersonaStore } from '../../src/store/persona';
|
||||
|
||||
// 模拟 usePersonaStore
|
||||
// emulation usePersonaStore
|
||||
vi.mock('../../src/store/persona', () => ({
|
||||
usePersonaStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
@@ -38,19 +38,19 @@ describe('save utils', () => {
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
|
||||
// 验证系统消息
|
||||
// Verify system message
|
||||
expect(result[0]).toEqual({
|
||||
prompt_type: PromptType.SYSTEM,
|
||||
data: '模拟的系统消息',
|
||||
});
|
||||
|
||||
// 验证用户前缀
|
||||
// validate user prefix
|
||||
expect(result[1]).toEqual({
|
||||
prompt_type: PromptType.USERPREFIX,
|
||||
data: '',
|
||||
});
|
||||
|
||||
// 验证用户后缀
|
||||
// validate user suffix
|
||||
expect(result[2]).toEqual({
|
||||
prompt_type: PromptType.USERSUFFIX,
|
||||
data: '',
|
||||
|
||||
@@ -20,7 +20,7 @@ import { setterActionFactory } from '../../src/utils/setter-factory';
|
||||
|
||||
describe('setterActionFactory', () => {
|
||||
it('应该创建一个增量更新函数', () => {
|
||||
// 创建模拟的 set 函数
|
||||
// Create a simulated set function
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater({ a: 1, b: 2 });
|
||||
@@ -28,37 +28,37 @@ describe('setterActionFactory', () => {
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
// Create a setter function
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
// Call setter for incremental update
|
||||
setter({ a: 3 });
|
||||
|
||||
// 验证 set 函数被调用
|
||||
// Verify that the set function is called
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
// Verify the updated status
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater({ a: 1, b: 2 });
|
||||
expect(result).toEqual({ a: 3, b: 2 });
|
||||
});
|
||||
|
||||
it('应该创建一个全量更新函数', () => {
|
||||
// 创建模拟的 set 函数
|
||||
// Create a simulated set function
|
||||
const mockSet = vi.fn();
|
||||
|
||||
// 创建 setter 函数
|
||||
// Create a setter function
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行全量更新
|
||||
// Call setter for full update
|
||||
setter({ a: 3 }, { replace: true });
|
||||
|
||||
// 验证 set 函数被调用,并且传入了正确的参数
|
||||
// Verify that the set function is called and the correct parameters are passed in
|
||||
expect(mockSet).toHaveBeenCalledWith({ a: 3 });
|
||||
});
|
||||
|
||||
it('应该处理空对象的增量更新', () => {
|
||||
// 创建模拟的 set 函数
|
||||
// Create a simulated set function
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater({});
|
||||
@@ -66,43 +66,43 @@ describe('setterActionFactory', () => {
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
// Create a setter function
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
// Call setter for incremental update
|
||||
setter({ a: 1 });
|
||||
|
||||
// 验证 set 函数被调用
|
||||
// Verify that the set function is called
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
// Verify the updated status
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater({});
|
||||
expect(result).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('应该处理空对象的全量更新', () => {
|
||||
// 创建模拟的 set 函数
|
||||
// Create a simulated set function
|
||||
const mockSet = vi.fn();
|
||||
|
||||
// 创建 setter 函数
|
||||
// Create a setter function
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行全量更新
|
||||
// Call setter for full update
|
||||
setter({}, { replace: true });
|
||||
|
||||
// 验证 set 函数被调用,并且传入了正确的参数
|
||||
// Verify that the set function is called and the correct parameters are passed in
|
||||
expect(mockSet).toHaveBeenCalledWith({});
|
||||
});
|
||||
|
||||
it('应该处理复杂对象的增量更新', () => {
|
||||
// 创建一个复杂的初始状态
|
||||
// Create a complex initial state
|
||||
const initialState = {
|
||||
user: { name: 'John', age: 30 },
|
||||
settings: { theme: 'dark', notifications: true },
|
||||
};
|
||||
|
||||
// 创建模拟的 set 函数
|
||||
// Create a simulated set function
|
||||
const mockSet = vi.fn(updater => {
|
||||
if (typeof updater === 'function') {
|
||||
return updater(initialState);
|
||||
@@ -110,22 +110,22 @@ describe('setterActionFactory', () => {
|
||||
return updater;
|
||||
});
|
||||
|
||||
// 创建 setter 函数
|
||||
// Create a setter function
|
||||
const setter = setterActionFactory(mockSet);
|
||||
|
||||
// 调用 setter 进行增量更新
|
||||
// Call setter for incremental update
|
||||
setter({
|
||||
user: { name: 'Jane', age: 25 },
|
||||
});
|
||||
|
||||
// 验证 set 函数被调用
|
||||
// Verify that the set function is called
|
||||
expect(mockSet).toHaveBeenCalled();
|
||||
|
||||
// 验证更新后的状态
|
||||
// Verify the updated status
|
||||
const updater = mockSet.mock.calls[0][0];
|
||||
const result = updater(initialState);
|
||||
|
||||
// 检查结果是否正确合并了对象
|
||||
// Check if the result is correct and the objects are merged.
|
||||
expect(result).toEqual({
|
||||
user: { name: 'Jane', age: 25 },
|
||||
settings: { theme: 'dark', notifications: true },
|
||||
|
||||
@@ -19,7 +19,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { createStorage, storage } from '../../src/utils/storage';
|
||||
import { useCollaborationStore } from '../../src/store/collaboration';
|
||||
|
||||
// 模拟 useCollaborationStore
|
||||
// Analog useCollaborationStore
|
||||
vi.mock('../../src/store/collaboration', () => ({
|
||||
useCollaborationStore: {
|
||||
getState: vi.fn().mockReturnValue({
|
||||
@@ -32,7 +32,7 @@ describe('storage utils', () => {
|
||||
let mockStorage: Storage;
|
||||
|
||||
beforeEach(() => {
|
||||
// 创建模拟的 Storage 对象
|
||||
// Create a simulated Storage object
|
||||
mockStorage = {
|
||||
getItem: vi.fn(),
|
||||
setItem: vi.fn(),
|
||||
@@ -55,19 +55,19 @@ describe('storage utils', () => {
|
||||
prefix,
|
||||
);
|
||||
|
||||
// 测试设置值
|
||||
// test settings
|
||||
proxy.testKey = 'testValue';
|
||||
expect(mockStorage.setItem).toHaveBeenCalledWith(
|
||||
`${prefix}.testKey`,
|
||||
'testValue',
|
||||
);
|
||||
|
||||
// 测试获取值
|
||||
// Test Get Value
|
||||
(mockStorage.getItem as any).mockReturnValueOnce('storedValue');
|
||||
expect(proxy.testKey).toBe('storedValue');
|
||||
expect(mockStorage.getItem).toHaveBeenCalledWith(`${prefix}.testKey`);
|
||||
|
||||
// 测试删除值
|
||||
// Test Delete Value
|
||||
delete proxy.testKey;
|
||||
expect(mockStorage.removeItem).toHaveBeenCalledWith(`${prefix}.testKey`);
|
||||
});
|
||||
@@ -76,22 +76,22 @@ describe('storage utils', () => {
|
||||
const target: Record<string, any> = {};
|
||||
const proxy = createStorage<Record<string, any>>(mockStorage, target);
|
||||
|
||||
// 设置字符串值应该成功
|
||||
// Setting string value should succeed
|
||||
proxy.key1 = 'value1';
|
||||
expect(mockStorage.setItem).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 注意:在实际代码中,设置非字符串值会返回 false,但不会抛出错误
|
||||
// 在测试中,我们只验证 setItem 没有被再次调用
|
||||
// Note: In actual code, setting a non-string value will return false, but no error will be thrown
|
||||
// In the test, we only verify that setItem is not called again
|
||||
try {
|
||||
// 这里可能会抛出错误,但我们不关心错误本身
|
||||
// Errors may be thrown here, but we don't care about the errors themselves
|
||||
proxy.key2 = 123 as any;
|
||||
// 如果没有抛出错误,我们期望 setItem 不会被再次调用
|
||||
// If no error is thrown, we expect that setItem will not be called again
|
||||
} catch (e) {
|
||||
// 如果抛出错误,我们也期望 setItem 不会被再次调用
|
||||
// If an error is thrown, we also expect that setItem will not be called again
|
||||
console.log('捕获到错误,但这是预期的行为');
|
||||
}
|
||||
|
||||
// 无论是否抛出错误,我们都期望 setItem 不会被再次调用
|
||||
// Whether an error is thrown or not, we expect that setItem will not be called again
|
||||
expect(mockStorage.setItem).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -114,17 +114,17 @@ describe('storage utils', () => {
|
||||
|
||||
it('设置 baseVersion 应该打印错误', () => {
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {
|
||||
/* 空函数 */
|
||||
/* empty function */
|
||||
});
|
||||
|
||||
// 注意:在实际代码中,设置 baseVersion 会返回 false 并打印错误,但不会抛出错误
|
||||
// 在测试中,我们只验证 console.error 被调用
|
||||
// Note: In the actual code, setting baseVersion will return false and print an error, but no error will be thrown
|
||||
// In testing, we only verify that console.error is called
|
||||
try {
|
||||
// 这里可能会抛出错误,但我们不关心错误本身
|
||||
// Errors may be thrown here, but we don't care about the errors themselves
|
||||
storage.baseVersion = 'new-version';
|
||||
// 如果没有抛出错误,我们期望 console.error 被调用
|
||||
// If no error is thrown, we expect console.error to be called
|
||||
} catch (e) {
|
||||
// 如果抛出错误,我们也期望 console.error 被调用
|
||||
// If an error is thrown, we also expect console.error to be called
|
||||
console.log('捕获到错误,但这是预期的行为');
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { UIToast } from '@coze-arch/bot-semi';
|
||||
|
||||
import { hasBraces, verifyBracesAndToast } from '../../src/utils/submit';
|
||||
|
||||
// 模拟 UIToast 和 I18n
|
||||
// Analog UIToast and I18n
|
||||
vi.mock('@coze-arch/bot-semi', () => ({
|
||||
UIToast: {
|
||||
warning: vi.fn(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user