diff --git a/packages/core/client/src/collection-manager/interfaces/formula.ts b/packages/core/client/src/collection-manager/interfaces/formula.ts
index 6f99daac1..b9776e81d 100644
--- a/packages/core/client/src/collection-manager/interfaces/formula.ts
+++ b/packages/core/client/src/collection-manager/interfaces/formula.ts
@@ -28,9 +28,12 @@ export const formula: IField = {
type: 'string',
title: '{{t("Expression")}}',
required: true,
- description: '{{t("Input +、-、*、/、( ) to calculate, input @ to open field variables.")}}',
+ description: '{{t("Input +, -, *, /, ( ) to calculate, input @ to open field variables.")}}',
'x-component': 'Formula.Expression',
'x-decorator': 'FormItem',
+ 'x-component-props': {
+ 'supports': ['number', 'percent']
+ }
},
'uiSchema.x-component-props.step': {
type: 'string',
diff --git a/packages/core/client/src/collection-manager/interfaces/percent.ts b/packages/core/client/src/collection-manager/interfaces/percent.ts
index 7ecee77be..7ec198775 100644
--- a/packages/core/client/src/collection-manager/interfaces/percent.ts
+++ b/packages/core/client/src/collection-manager/interfaces/percent.ts
@@ -14,7 +14,7 @@ export const percent: IField = {
uiSchema: {
type: 'string',
// title,
- 'x-component': 'InputNumber',
+ 'x-component': 'Percent',
'x-component-props': {
stringMode: true,
step: '0',
diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts
index fe09725a2..fe126b55d 100644
--- a/packages/core/client/src/locale/zh_CN.ts
+++ b/packages/core/client/src/locale/zh_CN.ts
@@ -348,7 +348,8 @@ export default {
'Precision': '精确度',
'Formula mode': '计算方式',
'Expression': '表达式',
- 'Input +、-、*、/、( ) to calculate, input @ to open field variables.': '英文输入+、-、*、/、( ) 进行运算,输入@打开可用字段变量。',
+ 'Input +, -, *, /, ( ) to calculate, input @ to open field variables.': '英文输入+、-、*、/、( ) 进行运算,输入@打开可用字段变量。',
+ 'Formula error.': '公式验证错误。',
'Accept': '文件格式',
'Rich Text': '富文本',
'Junction collection': '中间表',
diff --git a/packages/core/client/src/schema-component/antd/compute/Compute.tsx b/packages/core/client/src/schema-component/antd/compute/Compute.tsx
deleted file mode 100644
index 8db6db9e1..000000000
--- a/packages/core/client/src/schema-component/antd/compute/Compute.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { onFormValuesChange } from '@formily/core';
-import { connect, mapReadPretty, useFieldSchema, useFormEffects } from '@formily/react';
-import { InputNumber } from 'antd';
-import React, { useState } from 'react';
-import * as math from 'mathjs';
-import _ from 'lodash';
-import { ReadPretty } from '../input-number/ReadPretty';
-import { useCollection, useCollectionField } from '../../../collection-manager/hooks';
-
-const AntdCompute = (props) => {
- const { value, onChange, step } = props;
- // const { expression } = useCollectionField();
- const { getField } = useCollection();
- const fieldSchema = useFieldSchema();
- const options = getField(fieldSchema.name);
- const { expression } = options;
- const [computeValue, setComputeValue] = useState(value);
-
- useFormEffects(() => {
- onFormValuesChange((form) => {
- const scope = _.cloneDeep(form.values);
- let result;
- try {
- result = math.evaluate(expression, scope);
- result = math.round(result, 9);
- } catch {}
- if (result) {
- setComputeValue(result);
- if (onChange) {
- onChange(result);
- }
- }
- })
- })
-
- return (
-
- );
-}
-
-export const Compute = connect(
- AntdCompute,
- mapReadPretty(ReadPretty)
-);
-
-export default Compute;
\ No newline at end of file
diff --git a/packages/core/client/src/schema-component/antd/compute/index.ts b/packages/core/client/src/schema-component/antd/compute/index.ts
deleted file mode 100644
index b0482c837..000000000
--- a/packages/core/client/src/schema-component/antd/compute/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './Compute';
\ No newline at end of file
diff --git a/packages/core/client/src/schema-component/antd/formula-input/FormulaInput.tsx b/packages/core/client/src/schema-component/antd/formula-input/FormulaInput.tsx
index 8e206aabf..4ae630a20 100644
--- a/packages/core/client/src/schema-component/antd/formula-input/FormulaInput.tsx
+++ b/packages/core/client/src/schema-component/antd/formula-input/FormulaInput.tsx
@@ -1,12 +1,20 @@
-import { connect, mapProps } from '@formily/react';
-import { Dropdown, Menu } from 'antd';
+import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
+import { useFormLayout } from '@formily/antd';
+import { Field, onFormSubmitValidateStart } from '@formily/core';
+import { connect, mapProps, mapReadPretty, useField, useFieldSchema, useFormEffects } from '@formily/react';
+import { isValid } from '@formily/shared';
+import { Button, Input, Popover, Tag, Menu, Dropdown } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import ContentEditable from 'react-contenteditable';
+import { useTranslation } from 'react-i18next';
+import * as math from 'mathjs';
import { useCollectionManager } from '../../../collection-manager/hooks';
import { useRecord } from '../../../record-provider';
const AntdFormulaInput = (props) => {
- const { value, onChange } = props;
+ const { value, onChange, supports } = props;
+ const field = useField();
+ const { t } = useTranslation();
const record = useRecord();
const { getCollectionFields } = useCollectionManager();
const fields = getCollectionFields(record.collectionName || record.name) as any[];
@@ -17,8 +25,10 @@ const AntdFormulaInput = (props) => {
const [html, setHtml] = useState(null);
const numColumns = new Map();
- fields.filter(field => field.interface === 'number').forEach(field => {
+ const scope = {};
+ fields.filter(field => supports.includes(field.interface)).forEach(field => {
numColumns.set(field.name, field.uiSchema.title);
+ scope[field.name] = 1;
})
const keys = Array.from(numColumns.keys());
@@ -32,7 +42,7 @@ const AntdFormulaInput = (props) => {
useEffect(() => {
if (onChange && formula) {
- let v = formula;
+ let v = formula || '';
numColumns.forEach((value, key) => {
v = v.replaceAll(value, key);
})
@@ -60,6 +70,9 @@ const AntdFormulaInput = (props) => {
const current = inputRef.current as any;
setFormula(e.currentTarget.textContent);
setHtml(current.innerHTML);
+ if (e.currentTarget.textContent == '' && onChange) {
+ onChange(null);
+ }
}
const handleKeyDown = (e) => {
@@ -78,6 +91,21 @@ const AntdFormulaInput = (props) => {
}
}
+ useFormEffects(() => {
+ onFormSubmitValidateStart(() => {
+ try {
+ math.evaluate(field.value, scope);
+ field.feedbacks = [];
+ } catch {
+ field.setFeedback({
+ type: 'error',
+ code: 'FormulaError',
+ messages: [t('Formula error.')],
+ });
+ }
+ })
+ })
+
return (
{
+ const { value, onChange } = props;
+
+ return (
+ {
+ if (onChange) {
+ onChange(v / 100);
+ }
+ }}
+ />
+ );
+ },
+ mapReadPretty((props) => {
+ return ();
+ }),
+);
diff --git a/packages/core/client/src/schema-component/antd/percent/index.ts b/packages/core/client/src/schema-component/antd/percent/index.ts
new file mode 100644
index 000000000..c3d9f84ac
--- /dev/null
+++ b/packages/core/client/src/schema-component/antd/percent/index.ts
@@ -0,0 +1 @@
+export * from './Percent';
\ No newline at end of file
diff --git a/packages/core/database/src/fields/formula-field.ts b/packages/core/database/src/fields/formula-field.ts
index 87c26f226..a5bbff295 100644
--- a/packages/core/database/src/fields/formula-field.ts
+++ b/packages/core/database/src/fields/formula-field.ts
@@ -8,7 +8,7 @@ export class FormulaField extends Field {
}
caculate(expression, scope) {
- let result;
+ let result = null;
try {
result = math.evaluate(expression, scope);
result = math.round(result, 9);
@@ -18,7 +18,6 @@ export class FormulaField extends Field {
async initFieldData({ transaction }) {
const { expression, name } = this.options;
- console.log('initFieldData', expression, name);
const records = await this.collection.repository.find({
order: [this.collection.model.primaryKeyAttribute],
@@ -56,9 +55,37 @@ export class FormulaField extends Field {
}
}
+ async updateFieldData(instance, { transaction }) {
+ if (this.collection.name === instance.collectionName && instance.name === this.options.name) {
+ this.options = Object.assign(this.options, instance.options);
+ const { name, expression } = this.options;
+
+ const records = await this.collection.repository.find({
+ order: [this.collection.model.primaryKeyAttribute],
+ transaction,
+ });
+
+ for (const record of records) {
+ const scope = record.toJSON();
+ const result = this.caculate(expression, scope);
+ await record.update(
+ {
+ [name]: result,
+ },
+ {
+ transaction,
+ silent: true,
+ hooks: false,
+ },
+ );
+ }
+ }
+ }
+
bind() {
super.bind();
this.on('afterSync', this.initFieldData.bind(this));
+ this.database.on('fields.afterUpdate', this.updateFieldData.bind(this));
this.on('beforeCreate', this.caculateField.bind(this));
this.on('beforeUpdate', this.caculateField.bind(this));
}
@@ -67,6 +94,7 @@ export class FormulaField extends Field {
super.unbind();
this.off('beforeCreate', this.caculateField.bind(this));
this.off('beforeUpdate', this.caculateField.bind(this));
+ this.database.off('fields.afterUpdate', this.updateFieldData.bind(this));
this.off('afterSync', this.initFieldData.bind(this));
}
}