chore(plugin-workflow): add translation (#345)

* chore(plugin-workflow): add translation

* fix(plugin-workflow): type
This commit is contained in:
Junyi 2022-04-30 10:06:25 +08:00 committed by GitHub
parent 19fc9a3822
commit 26f072e499
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 234 additions and 143 deletions

View File

@ -32,6 +32,7 @@ export default {
"Insert inner": "在里面插入", "Insert inner": "在里面插入",
"Delete": "删除", "Delete": "删除",
"UI editor": "界面配置", "UI editor": "界面配置",
"Collection": "数据表",
"Collections & Fields": "数据表配置", "Collections & Fields": "数据表配置",
"Roles & Permissions": "角色和权限", "Roles & Permissions": "角色和权限",
"Edit profile": "个人资料", "Edit profile": "个人资料",
@ -153,6 +154,10 @@ export default {
"is not": "不等于", "is not": "不等于",
"contains": "包含", "contains": "包含",
"does not contain": "不包含", "does not contain": "不包含",
"starts with": "开头是",
"not starts with": "开头不是",
"ends with": "结尾是",
"not ends with": "结尾不是",
"is empty": "为空", "is empty": "为空",
"is not empty": "不为空", "is not empty": "不为空",
"Edit chart": "编辑图表", "Edit chart": "编辑图表",
@ -300,8 +305,8 @@ export default {
'Insert before': '在前面插入', 'Insert before': '在前面插入',
'Insert after': '在后面插入', 'Insert after': '在后面插入',
'UI Editor': '界面配置', 'UI Editor': '界面配置',
'ASC': 'ASC', 'ASC': '升序',
'DESC': 'DESC', 'DESC': '降序',
'Add sort field': '添加排序字段', 'Add sort field': '添加排序字段',
'ID': 'ID', 'ID': 'ID',
'Drawer': '抽屉', 'Drawer': '抽屉',
@ -399,4 +404,60 @@ export default {
'Local storage': '本地存储', 'Local storage': '本地存储',
'Aliyun OSS': '阿里云 OSS', 'Aliyun OSS': '阿里云 OSS',
'Amazon S3': '亚马逊 S3', 'Amazon S3': '亚马逊 S3',
// plugins/workflow
'Workflow': '工作流',
'Configure workflow': '流程配置',
'Executions': '执行历史',
'Trigger type': '触发方式',
'Description': '描述',
'Status': '状态',
'Enabled': '启用',
'Disabled': '禁用',
'Trigger configuration': '触发器配置',
'Load failed': '加载失败',
'Model event': '数据表事件',
'Trigger on': '触发时机',
'After record added': '新增数据后',
'After record updated': '更新数据后',
'After record added or updated': '新增或更新数据后',
'After record deleted': '删除数据后',
'Changed fields': '发生变动的字段',
'Select the fields which changed will trigger the event only': '只有被选中的某个字段发生变动时才会触发。如果不选择,则表示任何字段变动时都会触发。新增或删除数据时,任意字段都被认为发生变动。',
'Match condition': '满足条件',
'End': '结束',
'Trigger context': '触发数据',
'Node result': '节点数据',
'Constant': '常量',
'Boolean': '逻辑值',
'String': '字符串',
'Comparison': '值比较',
'Arithmetic calculation': '算术运算',
'String operation': '字符串',
'On going': '进行中',
'Success': '成功',
'Failed': '失败',
'Canceled': '已取消',
'Node configuration': '配置节点',
'This node contains branches, deleting will also be preformed to them, are you sure?': '节点包含分支,将同时删除其所有分支下的子节点,确定继续?',
'Control': '流程控制',
'Collection operations': '数据表操作',
'Node type': '节点类型',
'Calculation': '运算',
'Configure calculation': '配置运算',
'Calculation result': '运算结果',
'Create record': '新增数据',
'Update record': '更新数据',
'Query record': '查询数据',
} }

View File

@ -6,9 +6,12 @@ import { addButtonClass, branchBlockClass, branchClass, nodeBlockClass, nodeCard
import { import {
useCollection, useCollection,
useCompile,
useResourceActionContext useResourceActionContext
} from '..'; } from '..';
import { Instruction, instructions, Node } from './nodes'; import { Instruction, instructions, Node } from './nodes';
import { t } from 'i18next';
import { useTranslation } from 'react-i18next';
@ -34,10 +37,11 @@ export function useFlowContext() {
} }
export function WorkflowCanvas() { export function WorkflowCanvas() {
const { t } = useTranslation();
const { data, refresh, loading } = useResourceActionContext(); const { data, refresh, loading } = useResourceActionContext();
if (!data?.data && !loading) { if (!data?.data && !loading) {
return <div></div>; return <div>{t('Load failed')}</div>;
} }
const { nodes = [], ...workflow } = data?.data ?? {}; const { nodes = [], ...workflow } = data?.data ?? {};
@ -56,7 +60,7 @@ export function WorkflowCanvas() {
<div className={branchBlockClass}> <div className={branchBlockClass}>
<Branch entry={entry} /> <Branch entry={entry} />
</div> </div>
<div className={cx(nodeCardClass)}></div> <div className={cx(nodeCardClass)}>{t('End')}</div>
</FlowContext.Provider> </FlowContext.Provider>
); );
} }
@ -93,6 +97,7 @@ interface AddButtonProps {
}; };
export function AddButton({ upstream, branchIndex = null }: AddButtonProps) { export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
const compile = useCompile();
const { resource } = useCollection(); const { resource } = useCollection();
const { data } = useResourceActionContext(); const { data } = useResourceActionContext();
const { onNodeAdded } = useFlowContext(); const { onNodeAdded } = useFlowContext();
@ -120,8 +125,8 @@ export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
} }
const groups = [ const groups = [
{ value: 'control', name: '流程控制' }, { value: 'control', name: '{{t("Control")}}' },
{ value: 'model', name: '数据表操作' }, { value: 'collection', name: '{{t("Collection operations")}}' },
]; ];
const instructionList = (Array.from(instructions.getValues()) as Instruction[]); const instructionList = (Array.from(instructions.getValues()) as Instruction[]);
@ -130,17 +135,17 @@ export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
<Dropdown trigger={['click']} overlay={ <Dropdown trigger={['click']} overlay={
<Menu onClick={ev => onCreate(ev)}> <Menu onClick={ev => onCreate(ev)}>
{groups.map(group => ( {groups.map(group => (
<Menu.ItemGroup key={group.value} title={group.name}> <Menu.ItemGroup key={group.value} title={compile(group.name)}>
{instructionList.filter(item => item.group === group.value).map(item => item.options {instructionList.filter(item => item.group === group.value).map(item => item.options
? ( ? (
<Menu.SubMenu key={item.type} title={item.title}> <Menu.SubMenu key={item.type} title={compile(item.title)}>
{item.options.map(option => ( {item.options.map(option => (
<Menu.Item key={option.key}>{option.label}</Menu.Item> <Menu.Item key={option.key}>{compile(option.label)}</Menu.Item>
))} ))}
</Menu.SubMenu> </Menu.SubMenu>
) )
: ( : (
<Menu.Item key={item.type}>{item.title}</Menu.Item> <Menu.Item key={item.type}>{compile(item.title)}</Menu.Item>
))} ))}
</Menu.ItemGroup> </Menu.ItemGroup>
))} ))}

View File

@ -1,12 +1,14 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useActionContext, useRecord } from '..'; import { useActionContext, useRecord } from '..';
export const WorkflowLink = () => { export const WorkflowLink = () => {
const { t } = useTranslation();
const { id } = useRecord(); const { id } = useRecord();
const { setVisible } = useActionContext(); const { setVisible } = useActionContext();
return ( return (
<Link to={`/admin/plugins/workflows/${id}`} onClick={() => setVisible(false)}></Link> <Link to={`/admin/plugins/workflows/${id}`} onClick={() => setVisible(false)}>{t('Configure workflow')}</Link>
); );
} }

View File

@ -15,7 +15,7 @@ const workflowCollection = {
name: 'title', name: 'title',
interface: 'input', interface: 'input',
uiSchema: { uiSchema: {
title: '流程名称', title: '{{t("Name")}}',
type: 'string', type: 'string',
'x-component': 'Input', 'x-component': 'Input',
required: true, required: true,
@ -66,7 +66,7 @@ export const WorkflowPage = () => {
name: 'title', name: 'title',
interface: 'input', interface: 'input',
uiSchema: { uiSchema: {
title: '节点名称', title: '{{t("Name")}}',
type: 'string', type: 'string',
'x-component': 'Input', 'x-component': 'Input',
}, },
@ -76,7 +76,7 @@ export const WorkflowPage = () => {
name: 'type', name: 'type',
interface: 'select', interface: 'select',
uiSchema: { uiSchema: {
title: '节点类型', title: '{{t("Node type")}}',
type: 'string', type: 'string',
'x-component': 'Select', 'x-component': 'Select',
required: true, required: true,

View File

@ -5,6 +5,7 @@ import { uid } from '@formily/shared';
import { PluginManager } from '../plugin-manager'; import { PluginManager } from '../plugin-manager';
import { ActionContext, SchemaComponent, useActionContext } from '../schema-component'; import { ActionContext, SchemaComponent, useActionContext } from '../schema-component';
import { WorkflowTable } from './WorkflowTable'; import { WorkflowTable } from './WorkflowTable';
import { useTranslation } from 'react-i18next';
const useCloseAction = () => { const useCloseAction = () => {
const { setVisible } = useActionContext(); const { setVisible } = useActionContext();
@ -25,7 +26,7 @@ const schema: ISchema = {
[uid()]: { [uid()]: {
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
type: 'void', type: 'void',
title: '工作流', title: '{{t("Workflow")}}',
properties: { properties: {
main: { main: {
type: 'void', type: 'void',
@ -37,12 +38,13 @@ const schema: ISchema = {
}; };
export const WorkflowShortcut = () => { export const WorkflowShortcut = () => {
const { t } = useTranslation();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
return ( return (
<ActionContext.Provider value={{ visible, setVisible }}> <ActionContext.Provider value={{ visible, setVisible }}>
<PluginManager.Toolbar.Item <PluginManager.Toolbar.Item
icon={<PartitionOutlined />} icon={<PartitionOutlined />}
title={'工作流'} title={t('Workflow')}
onClick={() => { onClick={() => {
setVisible(true); setVisible(true);
}} }}

View File

@ -9,6 +9,7 @@ import { instructions, useNodeContext } from "./nodes";
import { useFlowContext } from "./WorkflowCanvas"; import { useFlowContext } from "./WorkflowCanvas";
import { triggers } from "./triggers"; import { triggers } from "./triggers";
import { SchemaComponent, useCollectionManager, useCompile } from ".."; import { SchemaComponent, useCollectionManager, useCompile } from "..";
import { useTranslation } from "react-i18next";
function NullRender() { function NullRender() {
return null; return null;
@ -17,7 +18,7 @@ function NullRender() {
export const calculators = [ export const calculators = [
{ {
value: 'boolean', value: 'boolean',
title: '值比较', title: '{{t("Comparison")}}',
children: [ children: [
{ value: 'equal', name: '=' }, { value: 'equal', name: '=' },
{ value: 'notEqual', name: '≠' }, { value: 'notEqual', name: '≠' },
@ -29,7 +30,7 @@ export const calculators = [
}, },
{ {
value: 'number', value: 'number',
title: '算术运算', title: '{{t("Arithmetic calculation")}}',
children: [ children: [
{ value: 'add', name: '+' }, { value: 'add', name: '+' },
{ value: 'minus', name: '-' }, { value: 'minus', name: '-' },
@ -40,19 +41,19 @@ export const calculators = [
}, },
{ {
value: 'string', value: 'string',
title: '字符串', title: '{{t("String operation")}}',
children: [ children: [
{ value: 'includes', name: '包含' }, { value: 'includes', name: '{{t("contains")}}' },
{ value: 'notIncludes', name: '不包含' }, { value: 'notIncludes', name: '{{t("does not contain")}}' },
{ value: 'startsWith', name: '开头是' }, { value: 'startsWith', name: '{{t("starts with")}}' },
{ value: 'notStartsWith', name: '开头不是' }, { value: 'notStartsWith', name: '{{t("not starts with")}}' },
{ value: 'endsWith', name: '结尾是' }, { value: 'endsWith', name: '{{t("ends with")}}' },
{ value: 'notEndsWith', name: '结尾不是' } { value: 'notEndsWith', name: '{{t("not ends with")}}' }
] ]
}, },
{ {
value: 'date', value: 'date',
title: '日期', title: '{{t("Date")}}',
children: [] children: []
} }
]; ];
@ -77,7 +78,7 @@ export const BaseTypeSet = new Set(['boolean', 'number', 'string', 'date']);
const ConstantTypes = { const ConstantTypes = {
string: { string: {
title: '字符串', title: '{{t("String")}}',
value: 'string', value: 'string',
component({ onChange, type, options, value }) { component({ onChange, type, options, value }) {
return ( return (
@ -90,7 +91,7 @@ const ConstantTypes = {
default: '' default: ''
}, },
number: { number: {
title: '数字', title: '{{t("Number")}}',
value: 'number', value: 'number',
component({ onChange, type, options, value }) { component({ onChange, type, options, value }) {
return ( return (
@ -103,7 +104,7 @@ const ConstantTypes = {
default: 0 default: 0
}, },
boolean: { boolean: {
title: '逻辑值', title: '{{t("Boolean")}}',
value: 'boolean', value: 'boolean',
component({ onChange, type, options, value }) { component({ onChange, type, options, value }) {
return ( return (
@ -127,7 +128,7 @@ const ConstantTypes = {
export const VariableTypes = { export const VariableTypes = {
constant: { constant: {
title: '常量', title: '{{t("Constant")}}',
value: 'constant', value: 'constant',
options: Object.values(ConstantTypes).map(item => ({ options: Object.values(ConstantTypes).map(item => ({
value: item.value, value: item.value,
@ -155,7 +156,7 @@ export const VariableTypes = {
} }
}, },
$jobsMapByNodeId: { $jobsMapByNodeId: {
title: '节点数据', title: '{{t("Node result")}}',
value: '$jobsMapByNodeId', value: '$jobsMapByNodeId',
options() { options() {
const node = useNodeContext(); const node = useNodeContext();
@ -210,7 +211,7 @@ export const VariableTypes = {
} }
}, },
$context: { $context: {
title: '触发数据', title: '{{t("Trigger context")}}',
value: '$context', value: '$context',
component() { component() {
const { workflow } = useFlowContext(); const { workflow } = useFlowContext();
@ -252,6 +253,7 @@ export function Operand({
onChange, onChange,
children children
}: OperandProps) { }: OperandProps) {
const compile = useCompile();
const Types = useVariableTypes(); const Types = useVariableTypes();
const { type } = operand; const { type } = operand;
@ -279,7 +281,7 @@ export function Operand({
options={Object.values(Types).map((item: any) => { options={Object.values(Types).map((item: any) => {
const options = typeof item.options === 'function' ? item.options() : item.options; const options = typeof item.options === 'function' ? item.options() : item.options;
return { return {
label: item.title, label: compile(item.title),
value: item.value, value: item.value,
children: options, children: options,
disabled: options && !options.length, disabled: options && !options.length,
@ -303,6 +305,7 @@ export function Operand({
} }
export function Calculation({ calculator, operands = [], onChange }) { export function Calculation({ calculator, operands = [], onChange }) {
const { t } = useTranslation();
return ( return (
<VariableTypesContext.Provider value={VariableTypes}> <VariableTypesContext.Provider value={VariableTypes}>
<div className={css` <div className={css`
@ -321,9 +324,9 @@ export function Calculation({ calculator, operands = [], onChange }) {
<> <>
<Select value={calculator} onChange={v => onChange({ operands, calculator: v })}> <Select value={calculator} onChange={v => onChange({ operands, calculator: v })}>
{calculators.map(group => ( {calculators.map(group => (
<Select.OptGroup key={group.value} label={group.title}> <Select.OptGroup key={group.value} label={t(group.title)}>
{group.children.map(item => ( {group.children.map(item => (
<Select.Option key={item.value} value={item.value}>{item.name}</Select.Option> <Select.Option key={item.value} value={t(item.value)}>{item.name}</Select.Option>
))} ))}
</Select.OptGroup> </Select.OptGroup>
))} ))}
@ -341,7 +344,7 @@ export function Calculation({ calculator, operands = [], onChange }) {
export function VariableComponent({ value, onChange, renderSchemaComponent }) { export function VariableComponent({ value, onChange, renderSchemaComponent }) {
const VTypes = { ...VariableTypes, const VTypes = { ...VariableTypes,
constant: { constant: {
title: '常量', title: '{{t("Constant")}}',
value: 'constant', value: 'constant',
options: undefined options: undefined
} }
@ -372,6 +375,7 @@ export function VariableComponent({ value, onChange, renderSchemaComponent }) {
// NOTE: observer for watching useProps // NOTE: observer for watching useProps
export const CollectionFieldset = observer(({ value, onChange }: any) => { export const CollectionFieldset = observer(({ value, onChange }: any) => {
const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const { getCollectionFields } = useCollectionManager(); const { getCollectionFields } = useCollectionManager();
const { values: data } = useForm(); const { values: data } = useForm();
@ -384,7 +388,7 @@ export const CollectionFieldset = observer(({ value, onChange }: any) => {
const VTypes = { const VTypes = {
...VariableTypes, ...VariableTypes,
constant: { constant: {
title: '常量', title: '{{t("Constant")}}',
value: 'constant', value: 'constant',
options: undefined options: undefined
} }
@ -458,14 +462,14 @@ export const CollectionFieldset = observer(({ value, onChange }: any) => {
))} ))}
</Menu> </Menu>
}> }>
<Button icon={<PlusOutlined />}></Button> <Button icon={<PlusOutlined />}>{t('Add field')}</Button>
</Dropdown> </Dropdown>
) )
: null : null
} }
</> </>
) )
: <p></p> : <p>{t('Select collection')}</p>
} }
</fieldset> </fieldset>
); );

View File

@ -1,15 +1,16 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { Calculation } from '../calculators'; import { Calculation } from '../calculators';
export default { export default {
title: '运算', title: '{{t("Calculation")}}',
type: 'calculation', type: 'calculation',
group: 'control', group: 'control',
fieldset: { fieldset: {
'config.calculation': { 'config.calculation': {
type: 'object', type: 'object',
title: '配置计算', title: '{{t("Configure calculation")}}',
name: 'config.calculation', name: 'config.calculation',
required: true, required: true,
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
@ -27,6 +28,7 @@ export default {
} }
}, },
getter() { getter() {
return <div className={css`flex-shrink: 0`}></div>; const { t } = useTranslation();
return <div className={css`flex-shrink: 0`}>{t('Calculation result')}</div>;
} }
}; };

View File

@ -1,5 +1,6 @@
import { Select } from 'antd'; import { Select } from 'antd';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { useCollectionDataSource, useCollectionManager, useCompile } from '../..'; import { useCollectionDataSource, useCollectionManager, useCompile } from '../..';
import { BaseTypeSet, CollectionFieldset } from '../calculators'; import { BaseTypeSet, CollectionFieldset } from '../calculators';
import { collection, values } from '../schemas/collection'; import { collection, values } from '../schemas/collection';
@ -7,9 +8,9 @@ import { useFlowContext } from '../WorkflowCanvas';
export default { export default {
title: '新增数据', title: '{{t("Create record")}}',
type: 'create', type: 'create',
group: 'model', group: 'collection',
fieldset: { fieldset: {
'config.collection': { 'config.collection': {
...collection, ...collection,
@ -45,6 +46,7 @@ export default {
CollectionFieldset CollectionFieldset
}, },
getter({ type, options, onChange }) { getter({ type, options, onChange }) {
const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const { collections = [] } = useCollectionManager(); const { collections = [] } = useCollectionManager();
const { nodes } = useFlowContext(); const { nodes } = useFlowContext();
@ -52,7 +54,7 @@ export default {
const collection = collections.find(item => item.name === config.collection) ?? { fields: [] }; const collection = collections.find(item => item.name === config.collection) ?? { fields: [] };
return ( return (
<Select value={options.path} placeholder="选择字段" onChange={path => { <Select value={options.path} placeholder={t('Fields')} onChange={path => {
onChange({ type, options: { ...options, path } }); onChange({ type, options: { ...options, path } });
}}> }}>
{collection.fields {collection.fields

View File

@ -4,9 +4,9 @@ import { VariableComponent } from '../calculators';
import { collection, filter } from '../schemas/collection'; import { collection, filter } from '../schemas/collection';
export default { export default {
title: '删除数据', title: '{{t("Delete record")}}',
type: 'destroy', type: 'destroy',
group: 'model', group: 'collection',
fieldset: { fieldset: {
'config.collection': collection, 'config.collection': collection,
'config.params': { 'config.params': {

View File

@ -4,7 +4,8 @@ import { ISchema, useForm } from '@formily/react';
import { Registry } from '@nocobase/utils'; import { Registry } from '@nocobase/utils';
import { Button, Modal, Tag } from 'antd'; import { Button, Modal, Tag } from 'antd';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { SchemaComponent, useActionContext, useAPIClient, useCollection, useRequest, useResourceActionContext } from '../..'; import { useTranslation } from 'react-i18next';
import { SchemaComponent, useActionContext, useAPIClient, useCollection, useCompile, useRequest, useResourceActionContext } from '../..';
import { nodeBlockClass, nodeCardClass, nodeClass, nodeHeaderClass, nodeTitleClass } from '../style'; import { nodeBlockClass, nodeCardClass, nodeClass, nodeHeaderClass, nodeTitleClass } from '../style';
import { AddButton, useFlowContext } from '../WorkflowCanvas'; import { AddButton, useFlowContext } from '../WorkflowCanvas';
@ -109,6 +110,7 @@ export function Node({ data }) {
} }
export function RemoveButton() { export function RemoveButton() {
const { t } = useTranslation();
const { resource } = useCollection(); const { resource } = useCollection();
const current = useNodeContext(); const current = useNodeContext();
const { nodes, onNodeRemoved } = useFlowContext(); const { nodes, onNodeRemoved } = useFlowContext();
@ -123,11 +125,11 @@ export function RemoveButton() {
const hasBranches = !nodes.find(item => item.upstream === current && item.branchIndex != null); const hasBranches = !nodes.find(item => item.upstream === current && item.branchIndex != null);
const message = hasBranches const message = hasBranches
? '确定删除该节点吗?' ? t('Are you sure you want to delete it?')
: '节点包含分支,将同时删除其所有分支下的子节点,确定继续?'; : t('This node contains branches, deleting will also be preformed to them, are you sure?');
Modal.confirm({ Modal.confirm({
title: '删除节点', title: t('Delete'),
content: message, content: message,
onOk onOk
}); });
@ -146,6 +148,7 @@ export function RemoveButton() {
export function NodeDefaultView(props) { export function NodeDefaultView(props) {
const { data, children } = props; const { data, children } = props;
const compile = useCompile();
const instruction = instructions.get(data.type); const instruction = instructions.get(data.type);
return ( return (
@ -154,7 +157,7 @@ export function NodeDefaultView(props) {
<div className={cx(nodeCardClass)}> <div className={cx(nodeCardClass)}>
<div className={cx(nodeHeaderClass)}> <div className={cx(nodeHeaderClass)}>
<h4 className={cx(nodeTitleClass)}> <h4 className={cx(nodeTitleClass)}>
<Tag>{instruction.title}</Tag> <Tag>{compile(instruction.title)}</Tag>
<strong>{data.title}</strong> <strong>{data.title}</strong>
<span className="workflow-node-id">#{data.id}</span> <span className="workflow-node-id">#{data.id}</span>
</h4> </h4>
@ -169,7 +172,7 @@ export function NodeDefaultView(props) {
view: instruction.view, view: instruction.view,
config: { config: {
type: 'void', type: 'void',
title: '配置节点', title: '{{t("Node configuration")}}',
'x-component': 'Action.Link', 'x-component': 'Action.Link',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
@ -177,7 +180,7 @@ export function NodeDefaultView(props) {
properties: { properties: {
drawer: { drawer: {
type: 'void', type: 'void',
title: '配置节点', title: '{{t("Node configuration")}}',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'Form',
'x-decorator-props': { 'x-decorator-props': {
@ -192,7 +195,7 @@ export function NodeDefaultView(props) {
title: { title: {
type: 'string', type: 'string',
name: 'title', name: 'title',
title: '节点名称', title: '{{t("Name")}}',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Input', 'x-component': 'Input',
}, },

View File

@ -6,11 +6,12 @@ import { useCollectionDataSource, useCollectionManager, useCompile } from '../..
import { useFlowContext } from '../WorkflowCanvas'; import { useFlowContext } from '../WorkflowCanvas';
import { BaseTypeSet, VariableComponent } from '../calculators'; import { BaseTypeSet, VariableComponent } from '../calculators';
import { collection, filter } from '../schemas/collection'; import { collection, filter } from '../schemas/collection';
import { useTranslation } from 'react-i18next';
export default { export default {
title: '查询数据', title: '{{t("Query record")}}',
type: 'query', type: 'query',
group: 'model', group: 'collection',
fieldset: { fieldset: {
'config.collection': collection, 'config.collection': collection,
'config.multiple': { 'config.multiple': {
@ -43,6 +44,7 @@ export default {
VariableComponent VariableComponent
}, },
getter({ type, options, onChange }) { getter({ type, options, onChange }) {
const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const { collections = [] } = useCollectionManager(); const { collections = [] } = useCollectionManager();
const { nodes } = useFlowContext(); const { nodes } = useFlowContext();
@ -50,7 +52,7 @@ export default {
const collection = collections.find(item => item.name === config.collection) ?? { fields: [] }; const collection = collections.find(item => item.name === config.collection) ?? { fields: [] };
return ( return (
<Select value={options.path} placeholder="选择字段" onChange={path => { <Select value={options.path} placeholder={t('Fields')} onChange={path => {
onChange({ type, options: { ...options, path } }); onChange({ type, options: { ...options, path } });
}}> }}>
{collection.fields {collection.fields

View File

@ -3,9 +3,9 @@ import { CollectionFieldset, VariableComponent } from '../calculators';
import { collection, filter, values } from '../schemas/collection'; import { collection, filter, values } from '../schemas/collection';
export default { export default {
title: '更新数据', title: '{{t("Update record")}}',
type: 'update', type: 'update',
group: 'model', group: 'collection',
fieldset: { fieldset: {
'config.collection': collection, 'config.collection': collection,
'config.params': { 'config.params': {

View File

@ -5,7 +5,7 @@ import { useCollectionFilterOptions } from "../../collection-manager/action-hook
export const collection = { export const collection = {
type: 'string', type: 'string',
title: '数据表', title: '{{t("Collection")}}',
name: 'config.collection', name: 'config.collection',
required: true, required: true,
'x-reactions': ['{{useCollectionDataSource()}}'], 'x-reactions': ['{{useCollectionDataSource()}}'],
@ -15,7 +15,7 @@ export const collection = {
export const values = { export const values = {
type: 'object', type: 'object',
title: '数据内容', title: '{{t("Form values")}}',
name: 'config.params.values', name: 'config.params.values',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-decorator-props': { 'x-decorator-props': {
@ -29,7 +29,7 @@ export const values = {
export const filter = { export const filter = {
type: 'object', type: 'object',
title: '筛选条件', title: '{{t("Filter")}}',
name: 'config.params.filter', name: 'config.params.filter',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-decorator-props': { 'x-decorator-props': {

View File

@ -4,12 +4,16 @@ const collection = {
name: 'executions', name: 'executions',
fields: [ fields: [
{ {
type: 'number', interface: 'createdAt',
name: 'workflow', type: 'datetime',
interface: 'linkTo', // field: 'createdAt',
name: 'createdAt',
uiSchema: { uiSchema: {
title: '所属工作流', type: 'datetime',
type: 'string', title: '{{t("Created at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
} as ISchema, } as ISchema,
}, },
{ {
@ -17,15 +21,15 @@ const collection = {
name: 'status', name: 'status',
interface: 'select', interface: 'select',
uiSchema: { uiSchema: {
title: '执行状态', title: '{{t("Status")}}',
type: 'string', type: 'string',
'x-component': 'Select', 'x-component': 'Select',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
enum: [ enum: [
{ value: 0, label: '进行中' }, { value: 0, label: '{{t("On going")}}' },
{ value: 1, label: '已完成' }, { value: 1, label: '{{t("Success")}}' },
{ value: -1, label: '已失败' }, { value: -1, label: '{{t("Failed")}}' },
{ value: -2, label: '已取消' }, { value: -2, label: '{{t("Canceled")}}' },
], ],
} as ISchema, } as ISchema,
} }
@ -76,13 +80,13 @@ export const executionSchema = {
useDataSource: '{{ cm.useDataSourceFromRAC }}', useDataSource: '{{ cm.useDataSourceFromRAC }}',
}, },
properties: { properties: {
workflow: { createdAt: {
type: 'void', type: 'void',
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
properties: { properties: {
workflow: { createdAt: {
type: 'string', type: 'datetime',
'x-component': 'CollectionField', 'x-component': 'CollectionField',
'x-read-pretty': true, 'x-read-pretty': true,
}, },
@ -100,27 +104,27 @@ export const executionSchema = {
}, },
} }
}, },
actions: { // actions: {
type: 'void', // type: 'void',
title: '{{ t("Actions") }}', // title: '{{ t("Actions") }}',
'x-component': 'Table.Column', // 'x-component': 'Table.Column',
properties: { // properties: {
actions: { // actions: {
type: 'void', // type: 'void',
'x-component': 'Space', // 'x-component': 'Space',
'x-component-props': { // 'x-component-props': {
split: '|', // split: '|',
}, // },
properties: { // properties: {
config: { // config: {
type: 'void', // type: 'void',
title: '查看', // title: '查看',
'x-component': 'ExecutionLink' // 'x-component': 'ExecutionLink'
}, // },
} // }
} // }
} // }
} // }
} }
} }
} }

View File

@ -1,4 +1,5 @@
import { ISchema } from '@formily/react'; import { ISchema } from '@formily/react';
import { triggers } from '../triggers';
import { executionSchema } from './executions'; import { executionSchema } from './executions';
const collection = { const collection = {
@ -9,7 +10,7 @@ const collection = {
name: 'title', name: 'title',
interface: 'input', interface: 'input',
uiSchema: { uiSchema: {
title: '流程名称', title: '{{t("Name")}}',
type: 'string', type: 'string',
'x-component': 'Input', 'x-component': 'Input',
required: true, required: true,
@ -20,13 +21,14 @@ const collection = {
name: 'type', name: 'type',
interface: 'select', interface: 'select',
uiSchema: { uiSchema: {
title: '触发方式', title: '{{t("Trigger type")}}',
type: 'string', type: 'string',
'x-component': 'Select', 'x-component': 'Select',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
enum: [ enum: Array.from(triggers.getEntities()).map(([value, { title }]) => ({
{ value: 'model', label: '数据变动' } value,
], label: title,
})),
required: true, required: true,
} as ISchema, } as ISchema,
}, },
@ -35,7 +37,7 @@ const collection = {
name: 'description', name: 'description',
interface: 'textarea', interface: 'textarea',
uiSchema: { uiSchema: {
title: '描述', title: '{{t("Description")}}',
type: 'string', type: 'string',
'x-component': 'Input.TextArea', 'x-component': 'Input.TextArea',
} as ISchema, } as ISchema,
@ -45,11 +47,11 @@ const collection = {
name: 'enabled', name: 'enabled',
interface: 'radio', interface: 'radio',
uiSchema: { uiSchema: {
title: '状态', title: '{{t("Status")}}',
type: 'string', type: 'string',
enum: [ enum: [
{ label: '启用', value: true }, { label: '{{t("Enabled")}}', value: true },
{ label: '禁用', value: false }, { label: '{{t("Disabled")}}', value: false },
], ],
'x-component': 'Radio.Group', 'x-component': 'Radio.Group',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
@ -94,7 +96,7 @@ export const workflowSchema: ISchema = {
properties: { properties: {
delete: { delete: {
type: 'void', type: 'void',
title: '删除', title: '{{t("Delete")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction: '{{ cm.useBulkDestroyAction }}', useAction: '{{ cm.useBulkDestroyAction }}',
@ -106,7 +108,7 @@ export const workflowSchema: ISchema = {
}, },
create: { create: {
type: 'void', type: 'void',
title: '添加工作流', title: '{{t("Add new")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
@ -116,7 +118,7 @@ export const workflowSchema: ISchema = {
type: 'void', type: 'void',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'Form',
title: '添加工作流', title: '{{t("Add new")}}',
properties: { properties: {
title: { title: {
'x-component': 'CollectionField', 'x-component': 'CollectionField',
@ -219,25 +221,24 @@ export const workflowSchema: ISchema = {
properties: { properties: {
config: { config: {
type: 'void', type: 'void',
title: '配置流程',
'x-component': 'WorkflowLink' 'x-component': 'WorkflowLink'
}, },
// executions: { executions: {
// type: 'void', type: 'void',
// title: '执行历史', title: '{{t("Executions")}}',
// 'x-component': 'Action.Link', 'x-component': 'Action.Link',
// 'x-component-props': { 'x-component-props': {
// type: 'primary', type: 'primary',
// }, },
// properties: { properties: {
// drawer: { drawer: {
// type: 'void', type: 'void',
// title: '执行历史', title: '{{t("Executions")}}',
// 'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
// properties: executionSchema properties: executionSchema
// } }
// } }
// }, },
update: { update: {
type: 'void', type: 'void',
title: '{{ t("Edit") }}', title: '{{ t("Edit") }}',
@ -253,7 +254,7 @@ export const workflowSchema: ISchema = {
'x-decorator-props': { 'x-decorator-props': {
useValues: '{{ cm.useValuesFromRecord }}', useValues: '{{ cm.useValuesFromRecord }}',
}, },
title: '编辑工作流', title: '{{ t("Edit") }}',
properties: { properties: {
title: { title: {
'x-component': 'CollectionField', 'x-component': 'CollectionField',

View File

@ -3,7 +3,7 @@ import { ISchema, useForm } from "@formily/react";
import { cx } from "@emotion/css"; import { cx } from "@emotion/css";
import { Registry } from "@nocobase/utils"; import { Registry } from "@nocobase/utils";
import { SchemaComponent, useActionContext, useAPIClient, useRecord, useRequest, useResourceActionContext } from '../../'; import { SchemaComponent, useActionContext, useAPIClient, useCompile, useRecord, useRequest, useResourceActionContext } from '../../';
import model from './model'; import model from './model';
import { nodeCardClass } from "../style"; import { nodeCardClass } from "../style";
@ -42,9 +42,10 @@ export interface Trigger {
export const triggers = new Registry<Trigger>(); export const triggers = new Registry<Trigger>();
triggers.register('model', model); triggers.register(model.type, model);
export const TriggerConfig = () => { export const TriggerConfig = () => {
const compile = useCompile();
const { data } = useResourceActionContext(); const { data } = useResourceActionContext();
if (!data) { if (!data) {
return null; return null;
@ -53,17 +54,17 @@ export const TriggerConfig = () => {
const { title, fieldset, scope } = triggers.get(type); const { title, fieldset, scope } = triggers.get(type);
return ( return (
<div className={cx(nodeCardClass)}> <div className={cx(nodeCardClass)}>
<h4>{title}</h4> <h4>{compile(title)}</h4>
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'void', type: 'void',
title: '触发器配置', title: '{{t("Trigger configuration")}}',
'x-component': 'Action.Link', 'x-component': 'Action.Link',
name: 'drawer', name: 'drawer',
properties: { properties: {
drawer: { drawer: {
type: 'void', type: 'void',
title: '触发器配置', title: '{{t("Trigger configuration")}}',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'Form',
'x-decorator-props': { 'x-decorator-props': {

View File

@ -9,6 +9,7 @@ import { useCompile } from '../../schema-component';
import { useFlowContext } from '../WorkflowCanvas'; import { useFlowContext } from '../WorkflowCanvas';
import { BaseTypeSet } from '../calculators'; import { BaseTypeSet } from '../calculators';
import { collection, filter } from '../schemas/collection'; import { collection, filter } from '../schemas/collection';
import { useTranslation } from 'react-i18next';
function useCollectionFieldsDataSource() { function useCollectionFieldsDataSource() {
const compile = useCompile(); const compile = useCompile();
@ -32,30 +33,30 @@ function useCollectionFieldsDataSource() {
} }
export default { export default {
title: '数据表事件', title: '{{t("Model event")}}',
type: 'model', type: 'model',
fieldset: { fieldset: {
'config.collection': collection, 'config.collection': collection,
'config.mode': { 'config.mode': {
type: 'number', type: 'number',
title: '触发时机', title: '{{t("Trigger on")}}',
name: 'config.mode', name: 'config.mode',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Select', 'x-component': 'Select',
'x-component-props': { 'x-component-props': {
options: [ options: [
{ value: 1, label: '新增数据后' }, { value: 1, label: '{{t("After record added")}}' },
{ value: 2, label: '更新数据后' }, { value: 2, label: '{{t("After record updated")}}' },
{ value: 3, label: '新增或更新数据后' }, { value: 3, label: '{{t("After record added or updated")}}' },
{ value: 4, label: '删除数据后' } { value: 4, label: '{{t("After record deleted")}}' }
] ]
} }
}, },
'config.changed': { 'config.changed': {
type: 'array', type: 'array',
name: 'changed', name: 'changed',
title: '发生变动的字段', title: '{{t("Changed fields")}}',
description: '只有被选中的某个字段发生变动时才会触发。如果不选择,则表示任何字段变动时都会触发。新增或删除数据时,任意字段都被认为发生变动。', description: '{{t("Select the fields which changed will trigger the event only")}}',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Select', 'x-component': 'Select',
'x-component-props': { 'x-component-props': {
@ -68,7 +69,7 @@ export default {
'config.condition': { 'config.condition': {
...filter, ...filter,
name: 'config.condition', name: 'config.condition',
title: '满足条件' title: '{{t("Match condition")}}',
} }
}, },
scope: { scope: {
@ -76,6 +77,7 @@ export default {
useCollectionFieldsDataSource useCollectionFieldsDataSource
}, },
getter({ type, options, onChange }) { getter({ type, options, onChange }) {
const { t } = useTranslation();
const compile = useCompile(); const compile = useCompile();
const { collections = [] } = useCollectionManager(); const { collections = [] } = useCollectionManager();
const { workflow } = useFlowContext(); const { workflow } = useFlowContext();
@ -83,7 +85,7 @@ export default {
return ( return (
<Select <Select
placeholder="选择字段" placeholder={t('Fields')}
value={options?.path?.replace(/^data\./, '')} value={options?.path?.replace(/^data\./, '')}
onChange={(path) => { onChange={(path) => {
onChange({ type, options: { ...options, path: `data.${path}` } }); onChange({ type, options: { ...options, path: `data.${path}` } });

View File

@ -9,7 +9,7 @@ export default {
interface: 'string', interface: 'string',
type: 'string', type: 'string',
name: 'title', name: 'title',
title: '自动化名称', title: '工作流名称',
required: true required: true
}, },
{ {