fix: update formula field and percent field (#461)
* feat: update formula field and percent field * fix: remove console * fix: percent component & formula-field
This commit is contained in:
parent
dd6f0a6d84
commit
b9ba44f36e
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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': '中间表',
|
||||
|
@ -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 (
|
||||
<InputNumber readOnly value={computeValue} stringMode={true} step={step} />
|
||||
);
|
||||
}
|
||||
|
||||
export const Compute = connect(
|
||||
AntdCompute,
|
||||
mapReadPretty(ReadPretty)
|
||||
);
|
||||
|
||||
export default Compute;
|
@ -1 +0,0 @@
|
||||
export * from './Compute';
|
@ -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<Field>();
|
||||
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<string, string>();
|
||||
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 (
|
||||
<Dropdown overlay={menu} visible={dropdownVisible}>
|
||||
<ContentEditable
|
||||
|
@ -33,5 +33,5 @@ export * from './time-picker';
|
||||
export * from './tree-select';
|
||||
export * from './upload';
|
||||
export * from './formula-input';
|
||||
export * from './compute';
|
||||
export * from './percent';
|
||||
import './index.less';
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { connect, mapReadPretty } from '@formily/react';
|
||||
import { InputNumber } from 'antd';
|
||||
import React from 'react';
|
||||
import { ReadPretty } from '../input-number/ReadPretty';
|
||||
|
||||
export const Percent = connect(
|
||||
(props) => {
|
||||
const { value, onChange } = props;
|
||||
|
||||
return (
|
||||
<InputNumber
|
||||
{...props}
|
||||
addonAfter="%"
|
||||
value={value * 100}
|
||||
onChange={(v: any) => {
|
||||
if (onChange) {
|
||||
onChange(v / 100);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
mapReadPretty((props) => {
|
||||
return (<ReadPretty {...props} value={props.value ? props.value * 100 : null} />);
|
||||
}),
|
||||
);
|
@ -0,0 +1 @@
|
||||
export * from './Percent';
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user