).get(engine);
- const form = useForm();
- const val = () => {
- const scope = cloneDeep(form.values);
- try {
- return evaluate(expression, scope);
- } catch (error) {
- return null;
- }
- };
- const [value, setVal] = useState(() => {
- return val();
- });
- useFormEffects(() => {
- onFormValuesChange(() => {
- setVal(val());
- });
- });
- if (dataType === 'double') {
- return {toFixedByStep(value, props.step)}
;
- }
- return {value}
;
-};
-
-export const Result = (props: any) => {
- const field = useField();
+export const Result = (props) => {
+ const { value, ...others } = props;
const { getField } = useCollection();
const fieldSchema = useFieldSchema();
const options = getField(fieldSchema.name as string);
- return field.readPretty ? : ;
+ const { dataType, expression, engine = 'math.js' } = options || {};
+ const { evaluate } = (evaluators as Registry).get(engine);
+ useFormEffects(() => {
+ onFormValuesChange((form) => {
+ const scope = cloneDeep(form.values);
+ let v;
+ try {
+ v = evaluate(expression, scope);
+ v = toDbType(v, dataType);
+ } catch (error) {
+ v = null;
+ }
+ if ((v == null && value == null) || JSON.stringify(v) === JSON.stringify(value)) {
+ return;
+ }
+ // console.log(options.name, v, value, props.defaultValue);
+ form.setValuesIn(options.name, v);
+ });
+ });
+ const Component = TypedComponents[dataType] ?? InputString;
+ return ;
};
export default Result;
diff --git a/packages/plugins/formula-field/src/client/formula/index.tsx b/packages/plugins/formula-field/src/client/formula/index.tsx
index 9a59d66df..ddecd1b3d 100644
--- a/packages/plugins/formula-field/src/client/formula/index.tsx
+++ b/packages/plugins/formula-field/src/client/formula/index.tsx
@@ -1,9 +1,11 @@
+import { connect, mapReadPretty } from '@formily/react';
+
import Expression from './Expression';
import Result from './Result';
export const Formula = () => null;
-Formula.Result = Result;
Formula.Expression = Expression;
+Formula.Result = connect(Result);
export default Formula;
diff --git a/packages/plugins/formula-field/src/server/formula-field.ts b/packages/plugins/formula-field/src/server/formula-field.ts
index 62c982316..519ccc618 100644
--- a/packages/plugins/formula-field/src/server/formula-field.ts
+++ b/packages/plugins/formula-field/src/server/formula-field.ts
@@ -3,6 +3,8 @@ import { DataTypes } from 'sequelize';
import { BaseFieldOptions, Field } from '@nocobase/database';
import evaluators from '@nocobase/evaluators';
+import { DataTypeTransformers, toDbType } from '../utils';
+
export interface FormulaFieldOptions extends BaseFieldOptions {
type: 'formula',
engine: string;
@@ -10,199 +12,21 @@ export interface FormulaFieldOptions extends BaseFieldOptions {
}
const DataTypeMap = {
- boolean: {
- type: DataTypes.BOOLEAN,
- transformers: Boolean
- },
- integer: {
- type: DataTypes.INTEGER,
- transformers: {
- boolean(value: boolean) {
- return Number(value);
- },
- number(value: number) {
- return value >= 0 ? Math.floor(value) : Math.ceil(value);
- },
- bigint(value: bigint) {
- return Number(value);
- },
- string(value: string) {
- const result = Number.parseInt(value, 10);
- if (Number.isNaN(result) || !Number.isFinite(result)) {
- return null;
- }
- return result;
- },
- date(value: Date) {
- const result = value.valueOf();
- if (Number.isNaN(result)) {
- return null;
- }
- return result;
- }
- }
- },
- bigInt: {
- type: DataTypes.BIGINT,
- transformers: {
- boolean(value: boolean) {
- return Number(value);
- },
- number(value: number) {
- return Math.floor(value >= 0 ? Math.floor(value) : Math.ceil(value));
- },
- bigint(value: bigint) {
- return Number(value);
- },
- string(value: string) {
- const result = Number.parseInt(value, 10);
- if (Number.isNaN(result) || !Number.isFinite(result)) {
- return null;
- }
- return result;
- },
- date(value: Date) {
- const result = value.valueOf();
- if (Number.isNaN(result)) {
- return null;
- }
- return result;
- }
- }
- },
- double: {
- type: DataTypes.DOUBLE,
- transformers: {
- boolean(value: boolean) {
- return Number(value);
- },
- number(value: number) {
- return value;
- },
- bigint(value: bigint) {
- return Number(value);
- },
- string(value: string) {
- const result = Number.parseFloat(value);
- if (Number.isNaN(result) || !Number.isFinite(result)) {
- return null;
- }
- return result;
- },
- date(value: Date) {
- const result = value.valueOf();
- if (Number.isNaN(result)) {
- return null;
- }
- return result;
- }
- }
- },
- decimal: {
- type: DataTypes.DECIMAL,
- transformers: {
- boolean(value: boolean) {
- return Number(value);
- },
- number(value: number) {
- return value;
- },
- bigint(value: bigint) {
- return value;
- },
- date(value: Date) {
- const result = value.valueOf();
- if (Number.isNaN(result)) {
- return null;
- }
- return result;
- }
- }
- },
- string: {
- type: DataTypes.STRING,
- transformers: {
- boolean(value: boolean) {
- return value.toString();
- },
- number(value: number) {
- return value.toString();
- },
- bigint(value: bigint) {
- return value.toString();
- },
- string(value: string) {
- return value;
- },
- date(value: Date) {
- return value.toISOString();
- }
- }
- },
- date: {
- type: DataTypes.DATE(3),
- transformers: {
- boolean(value: boolean) {
- return null;
- },
- number(value: number) {
- const result = new Date(value);
- if (Number.isNaN(result.valueOf())) {
- return null;
- }
- return result;
- },
- bigint(value: bigint) {
- const result = new Date(Number(value));
- if (Number.isNaN(result.valueOf())) {
- return null;
- }
- return result;
- },
- string(value: string) {
- const ts = Date.parse(value);
- if (Number.isNaN(ts)) {
- return null;
- }
- return new Date(ts);
- },
- date(value: Date) {
- return new Date(value);
- }
- }
- },
+ boolean: DataTypes.BOOLEAN,
+ integer: DataTypes.INTEGER,
+ bigInt: DataTypes.BIGINT,
+ double: DataTypes.DOUBLE,
+ decimal: DataTypes.DECIMAL,
+ string: DataTypes.STRING,
+ date: DataTypes.DATE(3),
}
-function toDbType(value, type) {
- if (value == null) {
- return null;
- }
-
- let jsType: string = typeof value;
- if (jsType == 'object' && value instanceof Date) {
- jsType = 'date';
- }
-
- if (!DataTypeMap[type]) {
- return null;
- }
-
- const { transformers } = DataTypeMap[type];
-
- if (typeof transformers === 'function') {
- return transformers(value);
- }
-
- const transformer = transformers[jsType];
- return transformer ? transformer(value) : null;
-}
-
export class FormulaField extends Field {
get dataType() {
const { dataType } = this.options;
- return DataTypeMap[dataType]?.type ?? DataTypes.DOUBLE;
+ return DataTypeMap[dataType] ?? DataTypes.DOUBLE;
}
calculate(scope) {
diff --git a/packages/plugins/formula-field/src/utils/index.ts b/packages/plugins/formula-field/src/utils/index.ts
new file mode 100644
index 000000000..aa4beb167
--- /dev/null
+++ b/packages/plugins/formula-field/src/utils/index.ts
@@ -0,0 +1,168 @@
+export const DataTypeTransformers = {
+ boolean: Boolean,
+ integer: {
+ boolean(value: boolean) {
+ return Number(value);
+ },
+ number(value: number) {
+ return value >= 0 ? Math.floor(value) : Math.ceil(value);
+ },
+ bigint(value: bigint) {
+ return Number(value);
+ },
+ string(value: string) {
+ const result = Number.parseInt(value, 10);
+ if (Number.isNaN(result) || !Number.isFinite(result)) {
+ return null;
+ }
+ return result;
+ },
+ date(value: Date) {
+ const result = value.valueOf();
+ if (Number.isNaN(result)) {
+ return null;
+ }
+ return result;
+ }
+ },
+ bigInt: {
+ boolean(value: boolean) {
+ return Number(value);
+ },
+ number(value: number) {
+ return Math.floor(value >= 0 ? Math.floor(value) : Math.ceil(value));
+ },
+ bigint(value: bigint) {
+ return Number(value);
+ },
+ string(value: string) {
+ const result = Number.parseInt(value, 10);
+ if (Number.isNaN(result) || !Number.isFinite(result)) {
+ return null;
+ }
+ return result;
+ },
+ date(value: Date) {
+ const result = value.valueOf();
+ if (Number.isNaN(result)) {
+ return null;
+ }
+ return result;
+ }
+ },
+ double: {
+ boolean(value: boolean) {
+ return Number(value);
+ },
+ number(value: number) {
+ return value;
+ },
+ bigint(value: bigint) {
+ return Number(value);
+ },
+ string(value: string) {
+ const result = Number.parseFloat(value);
+ if (Number.isNaN(result) || !Number.isFinite(result)) {
+ return null;
+ }
+ return result;
+ },
+ date(value: Date) {
+ const result = value.valueOf();
+ if (Number.isNaN(result)) {
+ return null;
+ }
+ return result;
+ }
+ },
+ decimal: {
+ boolean(value: boolean) {
+ return Number(value);
+ },
+ number(value: number) {
+ return value;
+ },
+ bigint(value: bigint) {
+ return value;
+ },
+ date(value: Date) {
+ const result = value.valueOf();
+ if (Number.isNaN(result)) {
+ return null;
+ }
+ return result;
+ }
+ },
+ string: {
+ boolean(value: boolean) {
+ return value.toString();
+ },
+ number(value: number) {
+ return value.toString();
+ },
+ bigint(value: bigint) {
+ return value.toString();
+ },
+ string(value: string) {
+ return value;
+ },
+ date(value: Date) {
+ return value.toISOString();
+ }
+ },
+ date: {
+ boolean(value: boolean) {
+ return null;
+ },
+ number(value: number) {
+ const result = new Date(value);
+ if (Number.isNaN(result.valueOf())) {
+ return null;
+ }
+ return result;
+ },
+ bigint(value: bigint) {
+ const result = new Date(Number(value));
+ if (Number.isNaN(result.valueOf())) {
+ return null;
+ }
+ return result;
+ },
+ string(value: string) {
+ const ts = Date.parse(value);
+ if (Number.isNaN(ts)) {
+ return null;
+ }
+ return new Date(ts);
+ },
+ date(value: Date) {
+ return new Date(value);
+ }
+ }
+};
+
+
+
+export function toDbType(value: any, type: string) {
+ if (value == null) {
+ return null;
+ }
+
+ let jsType: string = typeof value;
+ if (jsType == 'object' && value instanceof Date) {
+ jsType = 'date';
+ }
+
+ const transformers = DataTypeTransformers[type];
+
+ if (!transformers) {
+ return null;
+ }
+
+ if (typeof transformers === 'function') {
+ return transformers(value);
+ }
+
+ const transformer = transformers[jsType];
+ return transformer ? transformer(value) : null;
+}