108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import { BaseFieldOptions, Field } from '@nocobase/database';
|
|
import * as math from 'mathjs';
|
|
import { DataTypes } from 'sequelize';
|
|
import { evaluate } from '../utils/evaluate';
|
|
|
|
export class MathFormulaField extends Field {
|
|
get dataType() {
|
|
const { dataType } = this.options;
|
|
return dataType === 'string' ? DataTypes.STRING : DataTypes.DOUBLE;
|
|
}
|
|
|
|
calculate(expression, scope) {
|
|
let result: any = null;
|
|
try {
|
|
result = evaluate(expression, scope);
|
|
result = math.round(result, 9);
|
|
} catch {}
|
|
return result;
|
|
}
|
|
|
|
initFieldData = async ({ transaction }) => {
|
|
const { expression, name } = 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.calculate(expression, scope);
|
|
if (result) {
|
|
await record.update(
|
|
{
|
|
[name]: result,
|
|
},
|
|
{
|
|
transaction,
|
|
silent: true,
|
|
hooks: false,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
calculateField = async (instance) => {
|
|
const { expression, name } = this.options;
|
|
const scope = instance.toJSON();
|
|
let result;
|
|
try {
|
|
result = evaluate(expression, scope);
|
|
result = math.round(result, 9);
|
|
} catch {}
|
|
if (result === 0 || result) {
|
|
instance.set(name, result);
|
|
}
|
|
};
|
|
|
|
updateFieldData = async (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.calculate(expression, scope);
|
|
await record.update(
|
|
{
|
|
[name]: result,
|
|
},
|
|
{
|
|
transaction,
|
|
silent: true,
|
|
hooks: false,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
bind() {
|
|
super.bind();
|
|
this.on('afterSync', this.initFieldData);
|
|
// TODO: should not depends on fields table (which is defined by other plugin)
|
|
this.database.on('fields.afterUpdate', this.updateFieldData);
|
|
this.on('beforeSave', this.calculateField);
|
|
}
|
|
|
|
unbind() {
|
|
super.unbind();
|
|
this.off('beforeSave', this.calculateField);
|
|
// TODO: should not depends on fields table
|
|
this.database.off('fields.afterUpdate', this.updateFieldData);
|
|
this.off('afterSync', this.initFieldData);
|
|
}
|
|
}
|
|
|
|
export interface MathFormulaFieldOptions extends BaseFieldOptions {
|
|
type: 'mathFormula';
|
|
expression: string;
|
|
}
|