chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -22,7 +22,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import { FieldItem } from '../../../src/components/base-form-materials/field-item';
|
||||
|
||||
describe('FieldItem', () => {
|
||||
// 测试基本渲染
|
||||
// Test basic rendering
|
||||
it('should render title', () => {
|
||||
const title = 'Test Title';
|
||||
render(<FieldItem title={title} />);
|
||||
@@ -30,14 +30,14 @@ describe('FieldItem', () => {
|
||||
expect(titleElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试必填标记渲染
|
||||
// Test required tag rendering
|
||||
it('should render required marker', () => {
|
||||
render(<FieldItem title="Test" required />);
|
||||
const requiredMarker = screen.getByText('*');
|
||||
expect(requiredMarker).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试提示信息渲染
|
||||
// Test prompt information rendering
|
||||
it('should render tooltip', () => {
|
||||
const tooltipText = 'This is a tooltip';
|
||||
const el = render(<FieldItem title="Test" tooltip={tooltipText} />);
|
||||
@@ -47,7 +47,7 @@ describe('FieldItem', () => {
|
||||
expect(tooltipIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试标签渲染
|
||||
// test label rendering
|
||||
it('should render tag', () => {
|
||||
const tagText = 'New';
|
||||
render(<FieldItem title="Test" tag={tagText} />);
|
||||
@@ -55,7 +55,7 @@ describe('FieldItem', () => {
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试描述信息渲染
|
||||
// Test description information rendering
|
||||
it('should render description', () => {
|
||||
const descriptionText = 'This is a description';
|
||||
render(<FieldItem title="Test" description={descriptionText} />);
|
||||
@@ -63,7 +63,7 @@ describe('FieldItem', () => {
|
||||
expect(descriptionElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试反馈信息渲染
|
||||
// Test feedback rendering
|
||||
it('should render feedback', () => {
|
||||
const feedbackText = 'This is a feedback';
|
||||
render(<FieldItem title="Test" feedback={feedbackText} />);
|
||||
@@ -71,7 +71,7 @@ describe('FieldItem', () => {
|
||||
expect(feedbackElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试子元素渲染
|
||||
// Test child element rendering
|
||||
it('should render children', () => {
|
||||
const childText = 'Child Content';
|
||||
render(<FieldItem title="Test">{childText}</FieldItem>);
|
||||
|
||||
@@ -23,7 +23,7 @@ import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { GroupCollapse } from '../../../src/components/base-form-materials/group-collapse/collapse';
|
||||
|
||||
describe('GroupCollapse', () => {
|
||||
// 测试基本渲染
|
||||
// Test basic rendering
|
||||
it('should render label and initial content', () => {
|
||||
const label = 'Test Label';
|
||||
const childContent = 'Child Content';
|
||||
@@ -36,7 +36,7 @@ describe('GroupCollapse', () => {
|
||||
expect(childElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试提示信息渲染
|
||||
// Test prompt information rendering
|
||||
it('should render tooltip', () => {
|
||||
const tooltipText = 'This is a tooltip';
|
||||
const el = render(
|
||||
@@ -51,7 +51,7 @@ describe('GroupCollapse', () => {
|
||||
expect(tooltipIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试额外内容渲染
|
||||
// Test Extra Content Rendering
|
||||
it('should render extra content', () => {
|
||||
const extraText = 'Extra Content';
|
||||
render(
|
||||
@@ -64,26 +64,26 @@ describe('GroupCollapse', () => {
|
||||
expect(extraElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试点击标题展开/折叠功能
|
||||
// Test click title to expand/collapse function
|
||||
it('should toggle content when clicking the title', () => {
|
||||
const childContent = 'Child Content';
|
||||
const { getByText, queryByText } = render(
|
||||
<GroupCollapse label="Test">{childContent}</GroupCollapse>,
|
||||
);
|
||||
|
||||
// 初始状态下内容应该可见
|
||||
// The content should be visible in the initial state
|
||||
expect(queryByText(childContent)).toBeInTheDocument();
|
||||
|
||||
// 点击标题
|
||||
// Click on the title
|
||||
act(() => {
|
||||
fireEvent.click(getByText('Test'));
|
||||
});
|
||||
|
||||
// 点击后内容应该隐藏
|
||||
// Content should be hidden after clicking
|
||||
expect(queryByText(childContent)).toBeNull();
|
||||
});
|
||||
|
||||
// 测试粘性状态样式
|
||||
// Test sticky state style
|
||||
it('should apply sticky class when out of viewport or closed', async () => {
|
||||
const { useInViewport } = await vi.importMock('ahooks');
|
||||
(useInViewport as any).mockReturnValue([false]);
|
||||
|
||||
@@ -19,19 +19,19 @@ import { describe, it, expect } from 'vitest';
|
||||
import { isFormSchemaPropertyEmpty } from '../../src/utils/is-property-empty';
|
||||
|
||||
describe('isFormSchemaPropertyEmpty', () => {
|
||||
// 测试空对象
|
||||
// Test an empty object
|
||||
it('should return true for an empty object', () => {
|
||||
const emptyObject = {};
|
||||
expect(isFormSchemaPropertyEmpty(emptyObject)).toBe(true);
|
||||
});
|
||||
|
||||
// 测试非空对象
|
||||
// Testing non-empty objects
|
||||
it('should return false for a non-empty object', () => {
|
||||
const nonEmptyObject = { key: 'value' };
|
||||
expect(isFormSchemaPropertyEmpty(nonEmptyObject)).toBe(false);
|
||||
});
|
||||
|
||||
// 测试非对象值
|
||||
// Testing non-object values
|
||||
it('should return true for non-object values', () => {
|
||||
const values = [null, undefined, 123, 'string', true, false, []];
|
||||
values.forEach(value => {
|
||||
|
||||
@@ -19,13 +19,13 @@ import { describe, it, expect } from 'vitest';
|
||||
import { stringifyFormValuesFromBacked } from '../../src/utils/stringify-form-values-from-backed';
|
||||
|
||||
describe('stringifyFormValuesFromBacked', () => {
|
||||
// 测试输入为空的情况
|
||||
// When the test input is empty
|
||||
it('should return undefined when input is null or undefined', () => {
|
||||
expect(stringifyFormValuesFromBacked(null as any)).toBeUndefined();
|
||||
expect(stringifyFormValuesFromBacked(undefined as any)).toBeUndefined();
|
||||
});
|
||||
|
||||
// 测试输入包含字符串和布尔值的情况
|
||||
// Test if the input contains strings and boolean values
|
||||
it('should return the same string and boolean values', () => {
|
||||
const input = {
|
||||
str: 'hello',
|
||||
@@ -38,7 +38,7 @@ describe('stringifyFormValuesFromBacked', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 测试输入包含对象和数组的情况
|
||||
// Test if the input contains objects and arrays
|
||||
it('should stringify objects and arrays', () => {
|
||||
const input = {
|
||||
obj: { key: 'value' },
|
||||
@@ -51,7 +51,7 @@ describe('stringifyFormValuesFromBacked', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 测试输入包含 null 和 undefined 的情况
|
||||
// Test if the input contains null and undefined
|
||||
it('should set null and undefined values to undefined in the result', () => {
|
||||
const input = {
|
||||
nullValue: null,
|
||||
|
||||
@@ -40,12 +40,12 @@ export const GroupCollapse: React.FC<
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const ref = useRef(null);
|
||||
/**
|
||||
* 探测标题是否处于 sticky 状态
|
||||
* Detect if the title is sticky
|
||||
*/
|
||||
const [inViewport] = useInViewport(ref);
|
||||
return (
|
||||
<div>
|
||||
{/* 探测元素 */}
|
||||
{/* probe element */}
|
||||
<div ref={ref} />
|
||||
{/* header */}
|
||||
<div
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 基础表单物料
|
||||
* basic form material
|
||||
*/
|
||||
export { InputNumber, InputNumberProps } from './input-number';
|
||||
export { InputString, type InputStringProps } from './input-string';
|
||||
|
||||
@@ -35,11 +35,11 @@ export interface InputNumberProps {
|
||||
onChange: (v?: string) => void;
|
||||
onBlur: () => void;
|
||||
onFocus: () => void;
|
||||
/** 整型 */
|
||||
/** integer */
|
||||
int?: boolean;
|
||||
}
|
||||
|
||||
/** 是否是合法的数字字符串 */
|
||||
/** Is it a legal numeric string? */
|
||||
function isValidNumber(str: string) {
|
||||
try {
|
||||
const value = new BigNumber(str);
|
||||
@@ -71,17 +71,17 @@ export const InputNumber: React.FC<InputNumberProps> = ({
|
||||
|
||||
const handleBlur = () => {
|
||||
if (props.value === '' || props.value === undefined) {
|
||||
/** 失焦时若值为空,则同时清空验证值 */
|
||||
/** If the value is empty when out of focus, the verification value is also cleared */
|
||||
verifiedRef.current = undefined;
|
||||
if (props.value === '') {
|
||||
onChange(undefined);
|
||||
}
|
||||
} else {
|
||||
/** 失焦时若值不为空,则需要验证值的合法性 */
|
||||
/** If the value is not empty when out of focus, you need to verify the legitimacy of the value */
|
||||
/**
|
||||
* 1. 若值本身合法,则对值做格式化
|
||||
* 2. 若值不合法,则采纳最近一次的合法值
|
||||
* 3. 若都没有,则返回 undefined
|
||||
* 1. If the value itself is legal, format the value
|
||||
* 2. If the value is not legal, the most recent legal value is adopted
|
||||
* 3. If none, return undefined
|
||||
*/
|
||||
let next: undefined | string;
|
||||
const nextBig = normalizeNumber(props.value) || verifiedRef.current;
|
||||
@@ -113,7 +113,7 @@ export const InputNumber: React.FC<InputNumberProps> = ({
|
||||
onChange(next);
|
||||
};
|
||||
|
||||
/** 当值发生变化,需要把值同步到合法数字 */
|
||||
/** When the value changes, you need to synchronize the value to a legal number */
|
||||
useEffect(() => {
|
||||
if (props.value === '' || props.value === undefined) {
|
||||
verifiedRef.current = undefined;
|
||||
|
||||
@@ -32,7 +32,7 @@ export const FieldItem: React.FC<React.PropsWithChildren<FieldItemProps>> = ({
|
||||
const schema = useFieldSchema();
|
||||
|
||||
const isBatchField = schema.path.includes(TestFormFieldName.Batch);
|
||||
/** 批处理变量 tag 增加额外描述 */
|
||||
/** Batch variable tag adds extra description */
|
||||
const currentTag =
|
||||
tag && isBatchField
|
||||
? `${tag} - ${I18n.t('workflow_detail_node_batch')}`
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 表单物料
|
||||
* form material
|
||||
*/
|
||||
export { InputString } from './input-string';
|
||||
export { FieldItem } from './field-item';
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 固定的内部 field name
|
||||
* Fixed internal field name
|
||||
*/
|
||||
export enum TestFormFieldName {
|
||||
Node = '_node',
|
||||
@@ -23,7 +23,7 @@ export enum TestFormFieldName {
|
||||
Input = '_input',
|
||||
Setting = '_setting',
|
||||
JSON = '_json',
|
||||
/** 关联内容 */
|
||||
/** Linked Content */
|
||||
Related = '_related',
|
||||
Bot = '_bot',
|
||||
Conversation = '_conversation',
|
||||
|
||||
@@ -26,7 +26,7 @@ import { type StoreApi } from 'zustand';
|
||||
import { type IFormSchema } from '../form-engine';
|
||||
|
||||
/**
|
||||
* 单一表单内的全局性质状态集中管理
|
||||
* Global nature state centralized management within a single form
|
||||
*/
|
||||
export interface TestRunFormState {
|
||||
schema: IFormSchema | null;
|
||||
|
||||
@@ -31,7 +31,7 @@ interface ReactiveFieldProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 接入响应式的 Field
|
||||
* Access Responsive Fields
|
||||
*/
|
||||
const ReactiveField: React.FC<ReactiveFieldProps> = ({ parentUIState }) => {
|
||||
const components = useComponents();
|
||||
@@ -41,7 +41,7 @@ const ReactiveField: React.FC<ReactiveFieldProps> = ({ parentUIState }) => {
|
||||
const formUIState = useFormUIState();
|
||||
const fieldState = useCurrentFieldState();
|
||||
/**
|
||||
* 自生的 disabled 态由父亲和自身一起控制
|
||||
* The autologous disabled state is controlled by the father along with the self
|
||||
*/
|
||||
const disabled =
|
||||
parentUIState?.disabled || uiState.disabled || formUIState.disabled;
|
||||
|
||||
@@ -27,7 +27,7 @@ const computePath = (path?: string[], name?: string) =>
|
||||
[...(path || []), name].filter((i): i is string => Boolean(i));
|
||||
|
||||
/**
|
||||
* 递归 Field
|
||||
* Recursive Field
|
||||
*/
|
||||
const RecursionField: React.FC<RecursionFieldProps> = ({ name, schema }) => {
|
||||
const renderProperties = () => {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 表单引擎
|
||||
* form engine
|
||||
*/
|
||||
export { createSchemaField } from './fields';
|
||||
export { FormSchema } from './shared';
|
||||
|
||||
@@ -26,7 +26,7 @@ interface PropertyWithKey {
|
||||
}
|
||||
|
||||
export class FormSchema implements IFormSchema {
|
||||
/** IFormSchema 透传属性 */
|
||||
/** IFormSchema pass-through properties */
|
||||
type?: string | undefined;
|
||||
title?: ReactNode;
|
||||
description?: ReactNode;
|
||||
@@ -34,7 +34,7 @@ export class FormSchema implements IFormSchema {
|
||||
properties?: Record<string, IFormSchema>;
|
||||
defaultValue?: any;
|
||||
|
||||
/** 模型属性 */
|
||||
/** Model Properties */
|
||||
uiState = new ReactiveState<FormSchemaUIState>({ disabled: false });
|
||||
path: string[] = [];
|
||||
|
||||
@@ -64,7 +64,7 @@ export class FormSchema implements IFormSchema {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得有序的 properties
|
||||
* Obtain ordered properties
|
||||
*/
|
||||
static getProperties(schema: FormSchema | IFormSchema) {
|
||||
const orderProperties: PropertyWithKey[] = [];
|
||||
|
||||
@@ -33,65 +33,65 @@ export interface FormSchemaUIState {
|
||||
|
||||
export interface IFormSchema<FrameworkComponent = React.ReactNode> {
|
||||
/*******************************************************
|
||||
* 核心属性
|
||||
* core attributes
|
||||
*/
|
||||
version?: string;
|
||||
name?: string;
|
||||
type?: FormSchemaTypes;
|
||||
/** 默认值,“default” 是 jsonSchema 标准字段,但其为 js 关键字,遂使用 defaultValue */
|
||||
/** Default value, "default" is the jsonSchema standard field, but it is the js keyword, so defaultValue is used */
|
||||
defaultValue?: any;
|
||||
|
||||
/*******************************************************
|
||||
* 下钻属性
|
||||
* drill down properties
|
||||
*/
|
||||
properties?: Record<string, IFormSchema<FrameworkComponent>>;
|
||||
items?: IFormSchema<FrameworkComponent>[];
|
||||
|
||||
/*******************************************************
|
||||
* ui 属性
|
||||
* UI attribute
|
||||
*/
|
||||
title?: FrameworkComponent | string;
|
||||
description?: FrameworkComponent | string;
|
||||
/** 顺序 */
|
||||
/** order */
|
||||
['x-index']?: number;
|
||||
['x-visible']?: boolean;
|
||||
['x-hidden']?: boolean;
|
||||
['x-disabled']?: boolean;
|
||||
/** 渲染的组件 */
|
||||
/** Rendered components */
|
||||
['x-component']?: string;
|
||||
['x-component-props']?: Record<string, unknown>;
|
||||
/** 装饰器 */
|
||||
/** decorator */
|
||||
['x-decorator']?: string;
|
||||
['x-decorator-props']?: Record<string, unknown>;
|
||||
|
||||
/*******************************************************
|
||||
* 合法性属性
|
||||
* Legitimacy Attribute
|
||||
*/
|
||||
required?: boolean;
|
||||
['x-validator']?: IFormSchemaValidate;
|
||||
|
||||
/*******************************************************
|
||||
* 不常用或实现成本较高
|
||||
* Less commonly used or more expensive to implement
|
||||
*/
|
||||
['x-reactions']?: any;
|
||||
['x-content']?: FrameworkComponent;
|
||||
/** 通配符字段 */
|
||||
/** wild-card field */
|
||||
patternProperties?: Record<string, IFormSchema<FrameworkComponent>>;
|
||||
/** 定义之外的字段 */
|
||||
/** Fields outside the definition */
|
||||
additionalProperties?: IFormSchema<FrameworkComponent>;
|
||||
/** 定义之外的项 */
|
||||
/** Items outside the definition */
|
||||
additionalItems?: IFormSchema<FrameworkComponent>;
|
||||
|
||||
/*******************************************************
|
||||
* 业务自定义字段
|
||||
* business custom field
|
||||
*/
|
||||
/** 节点 id */
|
||||
/** Node ID */
|
||||
['x-node-id']?: string;
|
||||
/** 节点类型 */
|
||||
/** Node type */
|
||||
['x-node-type']?: string;
|
||||
/** 表单模式 */
|
||||
/** form mode */
|
||||
['x-form-mode']?: 'form' | 'json';
|
||||
/** 字段对应变量原始类型 */
|
||||
/** Field corresponding variable primitive type */
|
||||
['x-origin-type']?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -25,13 +25,13 @@ export const generateFieldComponent = (
|
||||
options: GenerateFieldComponentOptions,
|
||||
) => {
|
||||
const { type, validateJsonSchema } = options;
|
||||
/** 音色类型 */
|
||||
/** timbre type */
|
||||
if (ViewVariableType.Voice === type) {
|
||||
return {
|
||||
['x-component']: 'SelectVoice',
|
||||
};
|
||||
}
|
||||
/** 文件类型 */
|
||||
/** file type */
|
||||
if (ViewVariableType.isFileType(type)) {
|
||||
const fileType = [
|
||||
ViewVariableType.Image,
|
||||
@@ -42,14 +42,14 @@ export const generateFieldComponent = (
|
||||
return {
|
||||
['x-component']: 'TypedFileInput',
|
||||
['x-component-props']: {
|
||||
// 如果是数组类型,则表明是多选的文件选择器
|
||||
// If it is an array type, it indicates that it is a multi-selected file selector
|
||||
multiple: ViewVariableType.isArrayType(type),
|
||||
accept: getFileAccept(type),
|
||||
fileType,
|
||||
},
|
||||
};
|
||||
}
|
||||
/** 排除文件类型的对象类型、数组类型 */
|
||||
/** Exclude object types and array types for file types */
|
||||
if (ViewVariableType.isArrayType(type) || ViewVariableType.Object === type) {
|
||||
return {
|
||||
['x-component']: 'InputJson',
|
||||
@@ -80,7 +80,7 @@ export const generateFieldComponent = (
|
||||
['x-component']: 'InputTime',
|
||||
};
|
||||
}
|
||||
/** string 类型和其它未知类型都渲染普通输入框 */
|
||||
/** String type and other unknown types all render normal text boxes */
|
||||
return {
|
||||
['x-component']: 'InputString',
|
||||
};
|
||||
|
||||
@@ -29,8 +29,8 @@ interface GenerateFieldValidatorOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* ajv 实例缓存
|
||||
* 无需导入创建或者多次创建,优化内存开销
|
||||
* AJV instance cache
|
||||
* No need to import or create multiple times, optimizing memory overhead
|
||||
*/
|
||||
let ajvCache: undefined | Ajv;
|
||||
|
||||
@@ -45,7 +45,7 @@ export const generateFieldValidator = (
|
||||
param_name: title || name,
|
||||
});
|
||||
}
|
||||
// 如果有结构化描述,还需要对值进行反序列化校验
|
||||
// If there is a structured description, the value also needs to be deserialized
|
||||
if (validateJsonSchema && value !== undefined) {
|
||||
if (!ajvCache) {
|
||||
ajvCache = new Ajv();
|
||||
@@ -57,9 +57,9 @@ export const generateFieldValidator = (
|
||||
return valid ? undefined : I18n.t('workflow_debug_wrong_json');
|
||||
} catch {
|
||||
/**
|
||||
* 报错有多种可能,预期结果都是校验不通过
|
||||
* 1. 值反序列化失败
|
||||
* 2. 反序列化的值不合法
|
||||
* There are many possibilities for error reporting, and the expected result is that the verification fails.
|
||||
* 1. Value deserialization failed
|
||||
* 2. The deserialized value is not legal
|
||||
*/
|
||||
return I18n.t('workflow_debug_wrong_json');
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface GenerateFieldOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单 Field Schema 计算
|
||||
* Form Field Schema Calculation
|
||||
*/
|
||||
export const generateField = (options: GenerateFieldOptions): IFormSchema => {
|
||||
const {
|
||||
@@ -57,9 +57,9 @@ export const generateField = (options: GenerateFieldOptions): IFormSchema => {
|
||||
},
|
||||
['x-origin-type']: type as unknown as string,
|
||||
...generateFieldValidator(options),
|
||||
// 渲染组件相关
|
||||
// rendering component related
|
||||
...generateFieldComponent({ type, validateJsonSchema }),
|
||||
// component 也自带默认值,入参的默认值优先级更高
|
||||
// Component also comes with default values, and the default values of imported parameters have higher priority
|
||||
defaultValue,
|
||||
...extra,
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { isObject } from 'lodash-es';
|
||||
|
||||
/**
|
||||
* 是否是空的 properties
|
||||
* Is it an empty property?
|
||||
*/
|
||||
export const isFormSchemaPropertyEmpty = (properties: unknown) =>
|
||||
isObject(properties) ? !Object.keys(properties).length : true;
|
||||
|
||||
Reference in New Issue
Block a user