mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
Merge branch 'develop' into 2.0
This commit is contained in:
commit
407408b36b
@ -143,6 +143,17 @@ class Package {
|
|||||||
});
|
});
|
||||||
console.log(chalk.greenBright(`Downloaded: ${this.packageName}@${version}`));
|
console.log(chalk.greenBright(`Downloaded: ${this.packageName}@${version}`));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error.response.data && typeof error.response.data.pipe === 'function') {
|
||||||
|
let errorMessageBuffer = '';
|
||||||
|
error.response.data.on('data', (chunk) => {
|
||||||
|
errorMessageBuffer += chunk.toString('utf8'); // 收集错误信息
|
||||||
|
});
|
||||||
|
error.response.data.on('end', () => {
|
||||||
|
if (error.response.status === 403) {
|
||||||
|
console.error(chalk.redBright('You do not have permission to download this package version.'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
console.log(chalk.redBright(`Download failed: ${this.packageName}`));
|
console.log(chalk.redBright(`Download failed: ${this.packageName}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,7 +263,13 @@ module.exports = (cli) => {
|
|||||||
NOCOBASE_PKG_USERNAME,
|
NOCOBASE_PKG_USERNAME,
|
||||||
NOCOBASE_PKG_PASSWORD,
|
NOCOBASE_PKG_PASSWORD,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
const { accessKeyId, accessKeySecret } = await getAccessKeyPair();
|
let accessKeyId;
|
||||||
|
let accessKeySecret;
|
||||||
|
try {
|
||||||
|
({ accessKeyId, accessKeySecret } = await getAccessKeyPair());
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!(NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD) && !(accessKeyId && accessKeySecret)) {
|
if (!(NOCOBASE_PKG_USERNAME && NOCOBASE_PKG_PASSWORD) && !(accessKeyId && accessKeySecret)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ const fs = require('fs-extra');
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
const moment = require('moment-timezone');
|
const moment = require('moment-timezone');
|
||||||
const { keyDecrypt, getEnvAsync } = require('@nocobase/license-kit');
|
const { keyDecrypt, getEnvAsync } = require('@nocobase/license-kit');
|
||||||
const _ = require('lodash');
|
const omit = require('lodash/omit');
|
||||||
|
|
||||||
exports.isPackageValid = (pkg) => {
|
exports.isPackageValid = (pkg) => {
|
||||||
try {
|
try {
|
||||||
@ -491,10 +491,20 @@ exports.generatePlugins = function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function isEnvMatch(keyData) {
|
||||||
|
const env = await getEnvAsync();
|
||||||
|
if (env?.container?.id && keyData?.instanceData?.container?.id) {
|
||||||
|
return (
|
||||||
|
JSON.stringify(omit(env, ['timestamp', 'container', 'hostname'])) ===
|
||||||
|
JSON.stringify(omit(keyData?.instanceData, ['timestamp', 'container', 'hostname']))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return JSON.stringify(omit(env, ['timestamp'])) === JSON.stringify(omit(keyData?.instanceData, ['timestamp']));
|
||||||
|
}
|
||||||
|
|
||||||
exports.getAccessKeyPair = async function () {
|
exports.getAccessKeyPair = async function () {
|
||||||
const keyFile = resolve(process.cwd(), 'storage/.license/license-key');
|
const keyFile = resolve(process.cwd(), 'storage/.license/license-key');
|
||||||
if (!fs.existsSync(keyFile)) {
|
if (!fs.existsSync(keyFile)) {
|
||||||
// showLicenseInfo(LicenseKeyError.notExist);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,13 +515,13 @@ exports.getAccessKeyPair = async function () {
|
|||||||
keyData = JSON.parse(keyDataStr);
|
keyData = JSON.parse(keyDataStr);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showLicenseInfo(LicenseKeyError.parseFailed);
|
showLicenseInfo(LicenseKeyError.parseFailed);
|
||||||
return {};
|
throw new Error(LicenseKeyError.parseFailed.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentEnv = await getEnvAsync();
|
const isEnvMatched = await isEnvMatch(keyData);
|
||||||
if (!_.isEqual(_.omit(keyData?.instanceData, ['timestamp']), _.omit(currentEnv, ['timestamp']))) {
|
if (!isEnvMatched) {
|
||||||
showLicenseInfo(LicenseKeyError.notMatch);
|
showLicenseInfo(LicenseKeyError.notMatch);
|
||||||
return {};
|
throw new Error(LicenseKeyError.notMatch.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { accessKeyId, accessKeySecret } = keyData;
|
const { accessKeyId, accessKeySecret } = keyData;
|
||||||
@ -520,21 +530,21 @@ exports.getAccessKeyPair = async function () {
|
|||||||
|
|
||||||
const LicenseKeyError = {
|
const LicenseKeyError = {
|
||||||
notExist: {
|
notExist: {
|
||||||
title: 'License key not exist',
|
title: 'License key not found',
|
||||||
content:
|
content:
|
||||||
'Please go to the license settings page to obtain the Instance ID for the current environment, and then generate the license key on the service platform.',
|
'Please go to the license settings page to obtain the Instance ID for the current environment, and then generate the license key on the service platform.',
|
||||||
},
|
},
|
||||||
parseFailed: {
|
parseFailed: {
|
||||||
title: 'License key parse failed',
|
title: 'Invalid license key format',
|
||||||
content: 'Please check your license key, or regenerate the license key on the service platform.',
|
content: 'Please check your license key, or regenerate the license key on the service platform.',
|
||||||
},
|
},
|
||||||
notMatch: {
|
notMatch: {
|
||||||
title: 'License key not matched',
|
title: 'License key mismatch',
|
||||||
content:
|
content:
|
||||||
'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
|
'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
|
||||||
},
|
},
|
||||||
notValid: {
|
notValid: {
|
||||||
title: 'License key not valid',
|
title: 'Invalid license key',
|
||||||
content:
|
content:
|
||||||
'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
|
'Please go to the license settings page to obtain the Instance ID for the current environment, and then regenerate the license key on the service platform.',
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,7 @@ export class DateFieldInterface extends CollectionFieldInterface {
|
|||||||
'x-component': 'DatePicker',
|
'x-component': 'DatePicker',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
dateOnly: true,
|
dateOnly: true,
|
||||||
|
showTime: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import { useField, useForm } from '@formily/react';
|
import { useField, useForm } from '@formily/react';
|
||||||
import { Cascader, Input, Select, Spin, Table, Tag } from 'antd';
|
import { Cascader, Input, Select, Spin, Table, Tag } from 'antd';
|
||||||
import { last, omit } from 'lodash';
|
import _, { last, omit } from 'lodash';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ResourceActionContext, useCompile } from '../../../';
|
import { ResourceActionContext, useCompile } from '../../../';
|
||||||
@ -120,9 +120,10 @@ const PreviewCom = (props) => {
|
|||||||
}, [databaseView]);
|
}, [databaseView]);
|
||||||
|
|
||||||
const handleFieldChange = (record, index) => {
|
const handleFieldChange = (record, index) => {
|
||||||
dataSource.splice(index, 1, record);
|
const newDataSource = _.cloneDeep(dataSource);
|
||||||
setDataSource(dataSource);
|
newDataSource[index] = record;
|
||||||
field.value = dataSource.map((v) => {
|
setDataSource(newDataSource);
|
||||||
|
field.value = newDataSource.map((v) => {
|
||||||
const source = typeof v.source === 'string' ? v.source : v.source?.filter?.(Boolean)?.join('.');
|
const source = typeof v.source === 'string' ? v.source : v.source?.filter?.(Boolean)?.join('.');
|
||||||
return {
|
return {
|
||||||
...v,
|
...v,
|
||||||
@ -198,8 +199,7 @@ const PreviewCom = (props) => {
|
|||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
const interfaceConfig = getInterface(value);
|
handleFieldChange({ ...item, interface: value }, index);
|
||||||
handleFieldChange({ ...item, interface: value, uiSchema: interfaceConfig?.default?.uiSchema }, index);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data.map((group) => (
|
{data.map((group) => (
|
||||||
|
@ -66,7 +66,7 @@ export const createFormBlockSettings = new SchemaSettings({
|
|||||||
useVisible() {
|
useVisible() {
|
||||||
const { action } = useFormBlockContext();
|
const { action } = useFormBlockContext();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
return !action && schema?.['x-acl-action'].includes('create');
|
return !action && schema?.['x-acl-action']?.includes('create');
|
||||||
},
|
},
|
||||||
useComponentProps() {
|
useComponentProps() {
|
||||||
const { name } = useCollection_deprecated();
|
const { name } = useCollection_deprecated();
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { getDefaultFormat, str2moment, toGmt, toLocal, getPickerFormat } from '@nocobase/utils/client';
|
import { getDefaultFormat, str2moment, toGmt, toLocal, getPickerFormat } from '@nocobase/utils/client';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { dayjsable, formatDayjsValue } from '@formily/antd-v5/esm/__builtins__';
|
||||||
|
|
||||||
const toStringByPicker = (value, picker = 'date', timezone: 'gmt' | 'local') => {
|
const toStringByPicker = (value, picker = 'date', timezone: 'gmt' | 'local') => {
|
||||||
if (!dayjs.isDayjs(value)) return value;
|
if (!dayjs.isDayjs(value)) return value;
|
||||||
@ -89,7 +90,7 @@ export const handleDateChangeOnForm = (value, dateOnly, utc, picker, showTime, g
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (dateOnly) {
|
if (dateOnly) {
|
||||||
return dayjs(value).startOf(picker).format('YYYY-MM-DD');
|
return formatDayjsValue(value, 'YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
if (utc) {
|
if (utc) {
|
||||||
if (gmt) {
|
if (gmt) {
|
||||||
@ -114,6 +115,7 @@ export const mapDatePicker = function () {
|
|||||||
const { dateOnly, showTime, picker = 'date', utc, gmt, underFilter } = props;
|
const { dateOnly, showTime, picker = 'date', utc, gmt, underFilter } = props;
|
||||||
const format = getDefaultFormat(props);
|
const format = getDefaultFormat(props);
|
||||||
const onChange = props.onChange;
|
const onChange = props.onChange;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
inputReadOnly: isMobileMedia,
|
inputReadOnly: isMobileMedia,
|
||||||
|
@ -33,7 +33,7 @@ const toDate = (date, options: any = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (field.constructor.name === 'DateOnlyField') {
|
if (field.constructor.name === 'DateOnlyField') {
|
||||||
val = moment(val).format('YYYY-MM-DD HH:mm:ss');
|
val = moment.utc(val).format('YYYY-MM-DD HH:mm:ss');
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventObj = {
|
const eventObj = {
|
||||||
@ -69,7 +69,6 @@ export default {
|
|||||||
const r = parseDate(value, {
|
const r = parseDate(value, {
|
||||||
timezone: parseDateTimezone(ctx),
|
timezone: parseDateTimezone(ctx),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof r === 'string') {
|
if (typeof r === 'string') {
|
||||||
return {
|
return {
|
||||||
[Op.eq]: toDate(r, { ctx }),
|
[Op.eq]: toDate(r, { ctx }),
|
||||||
@ -77,6 +76,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(r)) {
|
if (Array.isArray(r)) {
|
||||||
|
console.log(11111111, {
|
||||||
|
[Op.and]: [{ [Op.gte]: toDate(r[0], { ctx }) }, { [Op.lt]: toDate(r[1], { ctx }) }],
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
[Op.and]: [{ [Op.gte]: toDate(r[0], { ctx }) }, { [Op.lt]: toDate(r[1], { ctx }) }],
|
[Op.and]: [{ [Op.gte]: toDate(r[0], { ctx }) }, { [Op.lt]: toDate(r[1], { ctx }) }],
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@ export interface Str2momentOptions {
|
|||||||
picker?: 'year' | 'month' | 'week' | 'quarter';
|
picker?: 'year' | 'month' | 'week' | 'quarter';
|
||||||
utcOffset?: number;
|
utcOffset?: number;
|
||||||
utc?: boolean;
|
utc?: boolean;
|
||||||
|
dateOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Str2momentValue = string | string[] | dayjs.Dayjs | dayjs.Dayjs[];
|
export type Str2momentValue = string | string[] | dayjs.Dayjs | dayjs.Dayjs[];
|
||||||
@ -83,10 +84,13 @@ const toMoment = (val: any, options?: Str2momentOptions) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const offset = options.utcOffset;
|
const offset = options.utcOffset;
|
||||||
const { gmt, picker, utc = true } = options;
|
const { gmt, picker, utc = true, dateOnly } = options;
|
||||||
|
|
||||||
if (dayjs(val).isValid()) {
|
if (dayjs(val).isValid()) {
|
||||||
|
if (dateOnly) {
|
||||||
|
return dayjs.utc(val, 'YYYY-MM-DD');
|
||||||
|
}
|
||||||
if (!utc) {
|
if (!utc) {
|
||||||
console.log(888);
|
|
||||||
return dayjs(val);
|
return dayjs(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,9 +188,11 @@ export const parseFilter = async (filter: any, opts: ParseFilterOptions = {}) =>
|
|||||||
const field = getField?.(path);
|
const field = getField?.(path);
|
||||||
|
|
||||||
if (field?.constructor.name === 'DateOnlyField' || field?.constructor.name === 'DatetimeNoTzField') {
|
if (field?.constructor.name === 'DateOnlyField' || field?.constructor.name === 'DatetimeNoTzField') {
|
||||||
|
if (value.type) {
|
||||||
|
return getDayRangeByParams({ ...value, timezone: field?.timezone || timezone });
|
||||||
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateValueWrapper(value, field?.timezone || timezone);
|
return dateValueWrapper(value, field?.timezone || timezone);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -63,7 +63,7 @@ const useSubmitProps = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
message.success(t('License key saved successfully, please restart the server'));
|
message.success(t('License key saved successfully, please re-run the plugin installation.'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"License key saved successfully, please restart the server": "License key saved successfully, please restart the server",
|
"License key saved successfully, please re-run the plugin installation.": "License key saved successfully, please re-run the plugin installation.",
|
||||||
"License settings": "License settings",
|
"License settings": "License settings",
|
||||||
"Instance ID": "Instance ID",
|
"Instance ID": "Instance ID",
|
||||||
"License key": "License key",
|
"License key": "License key",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"License key saved successfully, please restart the server": "授权密钥保存成功,请重启服务器",
|
"License key saved successfully, please re-run the plugin installation.": "授权密钥保存成功,请重新执行插件安装操作",
|
||||||
"License settings": "授权设置",
|
"License settings": "授权设置",
|
||||||
"Instance ID": "实例 ID",
|
"Instance ID": "实例 ID",
|
||||||
"License key": "授权密钥",
|
"License key": "授权密钥",
|
||||||
|
@ -30,13 +30,15 @@ import { WorkflowLink } from './WorkflowLink';
|
|||||||
import OpenDrawer from './components/OpenDrawer';
|
import OpenDrawer from './components/OpenDrawer';
|
||||||
import { workflowSchema } from './schemas/workflows';
|
import { workflowSchema } from './schemas/workflows';
|
||||||
import { ExecutionStatusSelect, ExecutionStatusColumn } from './components/ExecutionStatus';
|
import { ExecutionStatusSelect, ExecutionStatusColumn } from './components/ExecutionStatus';
|
||||||
import WorkflowPlugin, { ExecutionStatusOptions, RadioWithTooltip } from '.';
|
import WorkflowPlugin from '.';
|
||||||
|
import { RadioWithTooltip } from './components';
|
||||||
import { useRefreshActionProps } from './hooks/useRefreshActionProps';
|
import { useRefreshActionProps } from './hooks/useRefreshActionProps';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { TriggerOptionRender } from './components/TriggerOptionRender';
|
import { TriggerOptionRender } from './components/TriggerOptionRender';
|
||||||
import { CategoryTabs } from './WorkflowCategoryTabs';
|
import { CategoryTabs } from './WorkflowCategoryTabs';
|
||||||
import { EnumerationField } from './components/EmunerationField';
|
import { EnumerationField } from './components/EmunerationField';
|
||||||
import { useWorkflowFilterActionProps } from './hooks/useWorkflowFilterActionProps';
|
import { useWorkflowFilterActionProps } from './hooks/useWorkflowFilterActionProps';
|
||||||
|
import { ExecutionStatusOptions } from './constants';
|
||||||
|
|
||||||
function SyncOptionSelect(props) {
|
function SyncOptionSelect(props) {
|
||||||
const field = useField<any>();
|
const field = useField<any>();
|
||||||
|
@ -100,4 +100,5 @@ export default class extends Instruction {
|
|||||||
resultTitle: lang('Calculation result'),
|
resultTitle: lang('Calculation result'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
testable = true;
|
||||||
}
|
}
|
||||||
|
@ -194,4 +194,5 @@ export default class extends Instruction {
|
|||||||
</NodeDefaultView>
|
</NodeDefaultView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
testable = true;
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CloseOutlined, DeleteOutlined } from '@ant-design/icons';
|
import { CaretRightOutlined, CloseOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||||
import { createForm, Field } from '@formily/core';
|
import { createForm, Field } from '@formily/core';
|
||||||
import { toJS } from '@formily/reactive';
|
import { toJS } from '@formily/reactive';
|
||||||
import { ISchema, observer, useField, useForm } from '@formily/react';
|
import { ISchema, observer, useField, useForm } from '@formily/react';
|
||||||
import { Alert, App, Button, Dropdown, Empty, Input, Space, Tag, Tooltip, message } from 'antd';
|
import { Alert, App, Button, Collapse, Dropdown, Empty, Input, Space, Tag, Tooltip, message } from 'antd';
|
||||||
import { cloneDeep, get, set } from 'lodash';
|
import { cloneDeep, get, set } from 'lodash';
|
||||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -26,7 +26,6 @@ import {
|
|||||||
cx,
|
cx,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
useActionContext,
|
useActionContext,
|
||||||
useCancelAction,
|
|
||||||
useCompile,
|
useCompile,
|
||||||
usePlugin,
|
usePlugin,
|
||||||
useResourceActionContext,
|
useResourceActionContext,
|
||||||
@ -330,6 +329,7 @@ const useRunAction = () => {
|
|||||||
async run() {
|
async run() {
|
||||||
const template = parse(node.config);
|
const template = parse(node.config);
|
||||||
const config = template(toJS(values.config));
|
const config = template(toJS(values.config));
|
||||||
|
const logField = query('log').take() as Field;
|
||||||
const resultField = query('result').take() as Field;
|
const resultField = query('result').take() as Field;
|
||||||
resultField.setValue(null);
|
resultField.setValue(null);
|
||||||
resultField.setFeedback({});
|
resultField.setFeedback({});
|
||||||
@ -352,6 +352,7 @@ const useRunAction = () => {
|
|||||||
messages: data.status > 0 ? [lang('Resolved')] : [lang('Failed')],
|
messages: data.status > 0 ? [lang('Resolved')] : [lang('Failed')],
|
||||||
});
|
});
|
||||||
resultField.setValue(data.result);
|
resultField.setValue(data.result);
|
||||||
|
logField.setValue(data.log || '');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
resultField.setFeedback({
|
resultField.setFeedback({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@ -359,7 +360,6 @@ const useRunAction = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
ctx.setFormValueChanged(false);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -397,113 +397,163 @@ function TestFormFieldset({ value, onChange }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LogCollapse({ value }) {
|
||||||
|
return value ? (
|
||||||
|
<Collapse
|
||||||
|
ghost
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'log',
|
||||||
|
label: lang('Log'),
|
||||||
|
children: (
|
||||||
|
<Input.TextArea
|
||||||
|
value={value}
|
||||||
|
autoSize={{ minRows: 5, maxRows: 20 }}
|
||||||
|
style={{ whiteSpace: 'pre', cursor: 'text', fontFamily: 'monospace', fontSize: '80%' }}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
className={css`
|
||||||
|
.ant-collapse-item > .ant-collapse-header {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.ant-collapse-content > .ant-collapse-content-box {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useCancelAction() {
|
||||||
|
const form = useForm();
|
||||||
|
const ctx = useActionContext();
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
const resultField = form.query('result').take() as Field;
|
||||||
|
resultField.setFeedback();
|
||||||
|
form.setValues({ result: null, log: null });
|
||||||
|
form.clearFormGraph('*');
|
||||||
|
form.reset();
|
||||||
|
ctx.setVisible(false);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function TestButton() {
|
function TestButton() {
|
||||||
const node = useNodeContext();
|
const node = useNodeContext();
|
||||||
const { values } = useForm();
|
const { values } = useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
const template = parse(values);
|
const template = parse(values);
|
||||||
const keys = template.parameters.map((item) => item.key);
|
const keys = template.parameters.map((item) => item.key);
|
||||||
const form = useMemo(() => createForm(), []);
|
const form = useMemo(() => createForm(), []);
|
||||||
|
|
||||||
|
const onOpen = useCallback(() => {
|
||||||
|
setVisible(true);
|
||||||
|
}, []);
|
||||||
|
const setModalVisible = useCallback(
|
||||||
|
(v: boolean) => {
|
||||||
|
if (v) {
|
||||||
|
setVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resultField = form.query('result').take() as Field;
|
||||||
|
resultField?.setFeedback();
|
||||||
|
form.setValues({ result: null, log: null });
|
||||||
|
form.clearFormGraph('*');
|
||||||
|
form.reset();
|
||||||
|
setVisible(false);
|
||||||
|
},
|
||||||
|
[form],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeContext.Provider value={{ ...node, config: values }}>
|
<NodeContext.Provider value={{ ...node, config: values }}>
|
||||||
<VariableKeysContext.Provider value={keys}>
|
<VariableKeysContext.Provider value={keys}>
|
||||||
<SchemaComponent
|
<ActionContextProvider value={{ visible, setVisible: setModalVisible }}>
|
||||||
components={{
|
<Button icon={<CaretRightOutlined />} onClick={onOpen}>
|
||||||
Alert,
|
{lang('Test run')}
|
||||||
TestFormFieldset,
|
</Button>
|
||||||
}}
|
<SchemaComponent
|
||||||
scope={{
|
components={{
|
||||||
useCancelAction,
|
Alert,
|
||||||
useRunAction,
|
TestFormFieldset,
|
||||||
}}
|
LogCollapse,
|
||||||
schema={{
|
}}
|
||||||
type: 'void',
|
scope={{
|
||||||
name: 'testButton',
|
useCancelAction,
|
||||||
title: '{{t("Test run")}}',
|
useRunAction,
|
||||||
'x-component': 'Action',
|
}}
|
||||||
'x-component-props': {
|
schema={{
|
||||||
icon: 'CaretRightOutlined',
|
type: 'void',
|
||||||
// openSize: 'small',
|
name: 'modal',
|
||||||
},
|
'x-decorator': 'FormV2',
|
||||||
properties: {
|
'x-decorator-props': {
|
||||||
modal: {
|
form,
|
||||||
type: 'void',
|
},
|
||||||
'x-decorator': 'FormV2',
|
'x-component': 'Action.Modal',
|
||||||
'x-decorator-props': {
|
title: `{{t("Test run", { ns: "workflow" })}}`,
|
||||||
form,
|
properties: {
|
||||||
|
alert: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Alert',
|
||||||
|
'x-component-props': {
|
||||||
|
message: `{{t("Test run will do the actual data manipulating or API calling, please use with caution.", { ns: "workflow" })}}`,
|
||||||
|
type: 'warning',
|
||||||
|
showIcon: true,
|
||||||
|
className: css`
|
||||||
|
margin-bottom: 1em;
|
||||||
|
`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'x-component': 'Action.Modal',
|
config: {
|
||||||
title: `{{t("Test run", { ns: "workflow" })}}`,
|
type: 'object',
|
||||||
properties: {
|
title: '{{t("Replace variables", { ns: "workflow" })}}',
|
||||||
alert: {
|
'x-decorator': 'FormItem',
|
||||||
type: 'void',
|
'x-component': 'TestFormFieldset',
|
||||||
'x-component': 'Alert',
|
},
|
||||||
'x-component-props': {
|
actions: {
|
||||||
message: `{{t("Test run will do the actual data manipulating or API calling, please use with caution.", { ns: "workflow" })}}`,
|
type: 'void',
|
||||||
type: 'warning',
|
'x-component': 'ActionBar',
|
||||||
showIcon: true,
|
properties: {
|
||||||
className: css`
|
submit: {
|
||||||
margin-bottom: 1em;
|
type: 'void',
|
||||||
`,
|
title: '{{t("Run")}}',
|
||||||
},
|
'x-component': 'Action',
|
||||||
},
|
'x-component-props': {
|
||||||
config: {
|
type: 'primary',
|
||||||
type: 'object',
|
useAction: '{{ useRunAction }}',
|
||||||
title: '{{t("Replace variables", { ns: "workflow" })}}',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'TestFormFieldset',
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
properties: {
|
|
||||||
submit: {
|
|
||||||
type: 'void',
|
|
||||||
title: '{{t("Run")}}',
|
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'primary',
|
|
||||||
useAction: '{{ useRunAction }}',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: {
|
},
|
||||||
type: 'string',
|
result: {
|
||||||
title: `{{t("Result", { ns: "workflow" })}}`,
|
type: 'string',
|
||||||
'x-decorator': 'FormItem',
|
title: `{{t("Result", { ns: "workflow" })}}`,
|
||||||
'x-component': 'Input.JSON',
|
'x-decorator': 'FormItem',
|
||||||
'x-component-props': {
|
'x-component': 'Input.JSON',
|
||||||
autoSize: {
|
'x-component-props': {
|
||||||
minRows: 5,
|
autoSize: {
|
||||||
maxRows: 20,
|
minRows: 5,
|
||||||
},
|
maxRows: 20,
|
||||||
style: {
|
|
||||||
whiteSpace: 'pre',
|
|
||||||
cursor: 'text',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'x-pattern': 'disabled',
|
style: {
|
||||||
},
|
whiteSpace: 'pre',
|
||||||
footer: {
|
cursor: 'text',
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Action.Modal.Footer',
|
|
||||||
properties: {
|
|
||||||
cancel: {
|
|
||||||
type: 'void',
|
|
||||||
title: '{{t("Close")}}',
|
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {
|
|
||||||
useAction: '{{ useCancelAction }}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'x-pattern': 'disabled',
|
||||||
|
},
|
||||||
|
log: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'LogCollapse',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</ActionContextProvider>
|
||||||
</VariableKeysContext.Provider>
|
</VariableKeysContext.Provider>
|
||||||
</NodeContext.Provider>
|
</NodeContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -39,6 +39,22 @@ export class CalculationInstruction extends Instruction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async test({ engine = 'math.js', expression = '' }) {
|
||||||
|
const evaluator = <Evaluator | undefined>evaluators.get(engine);
|
||||||
|
try {
|
||||||
|
const result = evaluator && expression ? evaluator(expression) : null;
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
status: JOB_STATUS.RESOLVED,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
result: e.toString(),
|
||||||
|
status: JOB_STATUS.ERROR,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CalculationInstruction;
|
export default CalculationInstruction;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { evaluators } from '@nocobase/evaluators';
|
import { Evaluator, evaluators } from '@nocobase/evaluators';
|
||||||
import { Instruction } from '.';
|
import { Instruction } from '.';
|
||||||
import type Processor from '../Processor';
|
import type Processor from '../Processor';
|
||||||
import { JOB_STATUS } from '../constants';
|
import { JOB_STATUS } from '../constants';
|
||||||
@ -80,6 +80,22 @@ export class ConditionInstruction extends Instruction {
|
|||||||
// pass control to upper scope by ending current scope
|
// pass control to upper scope by ending current scope
|
||||||
return processor.exit(branchJob.status);
|
return processor.exit(branchJob.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async test({ engine, calculation, expression = '' }) {
|
||||||
|
const evaluator = <Evaluator | undefined>evaluators.get(engine);
|
||||||
|
try {
|
||||||
|
const result = evaluator ? evaluator(expression) : logicCalculate(calculation);
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
status: JOB_STATUS.RESOLVED,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
result: e.toString(),
|
||||||
|
status: JOB_STATUS.ERROR,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConditionInstruction;
|
export default ConditionInstruction;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user