@tachybase/plugin-field-formula 1.6.23 → 1.6.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,4 +6,6 @@ English | [中文](./README.zh-CN.md)
6
6
 
7
7
  内置插件无需手动安装激活。
8
8
 
9
+ 使用 `math.js` 表达式引擎时,需额外启用 `evaluator-mathjs` 插件。
10
+
9
11
  ## 使用方法
package/README.zh-CN.md CHANGED
@@ -6,4 +6,6 @@
6
6
 
7
7
  内置插件无需手动安装激活。
8
8
 
9
+ 使用 `math.js` 表达式引擎时,需额外启用 `evaluator-mathjs` 插件。
10
+
9
11
  ## 使用方法
@@ -1,4 +1,4 @@
1
- (function(n,r){typeof exports=="object"&&typeof module!="undefined"?r(exports,require("@tachybase/client"),require("@tachybase/schema"),require("react/jsx-runtime"),require("react"),require("@tego/client"),require("lodash"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","@tachybase/schema","react/jsx-runtime","react","@tego/client","lodash","react-i18next"],r):(n=typeof globalThis!="undefined"?globalThis:n||self,r(n["@tachybase/plugin-field-formula"]={},n["@tachybase/client"],n["@tachybase/schema"],n.jsxRuntime,n.react,n["@tego/client"],n.lodash))})(this,function(n,r,a,s,N,g,S){"use strict";var ae=Object.defineProperty,oe=Object.defineProperties;var ue=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable;var w=(n,r,a)=>r in n?ae(n,r,{enumerable:!0,configurable:!0,writable:!0,value:a}):n[r]=a,h=(n,r)=>{for(var a in r||(r={}))V.call(r,a)&&w(n,a,r[a]);if(C)for(var a of C(r))_.call(r,a)&&w(n,a,r[a]);return n},y=(n,r)=>oe(n,ue(r));var $=(n,r)=>{var a={};for(var s in n)V.call(n,s)&&r.indexOf(s)<0&&(a[s]=n[s]);if(n!=null&&C)for(var s of C(n))r.indexOf(s)<0&&_.call(n,s)&&(a[s]=n[s]);return a};var B=(n,r,a)=>new Promise((s,N)=>{var g=d=>{try{v(a.next(d))}catch(F){N(F)}},S=d=>{try{v(a.throw(d))}catch(F){N(F)}},v=d=>d.done?s(d.value):Promise.resolve(d.value).then(g,S);v((a=a.apply(n,r)).next())});const v=e=>{var b;const{value:t="",supports:i=[],useCurrentFields:o,onChange:u}=e,c=r.useCompile(),{getCollectionFields:p}=r.useCollectionManager_deprecated(),m=d(c,p,(b=o==null?void 0:o())!=null?b:[]);return s.jsx(r.Variable.TextArea,{value:t,onChange:u,scope:m})},d=(e,t,i)=>{const o=[];return i==null||i.forEach(u=>{var c,p,m,b;!["m2m","o2m"].includes(u.interface)&&["hasOne","hasMany","belongsTo","belongsToMany"].includes(u.type)&&u.uiSchema&&o.push({label:e(u.uiSchema.title),value:u.name,children:(p=(c=t(u.target))==null?void 0:c.filter(l=>l.uiSchema).map(l=>({label:l.uiSchema?e(l.uiSchema.title):"",value:l.name})))!=null?p:[]}),["hasOne","hasMany","belongsTo","belongsToMany"].includes(u.type)||o.push({label:e(u.uiSchema.title),value:u.name,children:(b=(m=t(u.target))==null?void 0:m.filter(l=>l.uiSchema).map(l=>({label:l.uiSchema?e(l.uiSchema.title):"",value:l.name})))!=null?b:[]})}),o},F={boolean:Boolean,integer:{boolean(e){return Number(e)},number(e){return e>=0?Math.floor(e):Math.ceil(e)},bigint(e){return Number(e)},string(e){const t=Number.parseInt(e,10);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},bigInt:{boolean(e){return Number(e)},number(e){return Math.floor(e>=0?Math.floor(e):Math.ceil(e))},bigint(e){return Number(e)},string(e){const t=Number.parseInt(e,10);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},double:{boolean(e){return Number(e)},number(e){return e},bigint(e){return Number(e)},string(e){const t=Number.parseFloat(e);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},decimal:{boolean(e){return Number(e)},number(e){return e},bigint(e){return e},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},string:{boolean(e){return e.toString()},number(e){return e.toString()},bigint(e){return e.toString()},string(e){return e},date(e){return e.toISOString()}},date:{boolean(e){return null},number(e){const t=new Date(e);return Number.isNaN(t.valueOf())?null:t},bigint(e){const t=new Date(Number(e));return Number.isNaN(t.valueOf())?null:t},string(e){const t=Date.parse(e);return Number.isNaN(t)?null:new Date(t)},date(e){return new Date(e)}}};function G(e,t){if(e==null)return null;let i=typeof e;i=="object"&&e instanceof Date&&(i="date");const o=F[t];if(!o)return null;if(typeof o=="function")return o(e);const u=o[i];return u?u(e):null}const J={boolean:r.Checkbox.ReadPretty,integer:r.InputNumber.ReadPretty,bigInt:r.InputNumber.ReadPretty,double:r.InputNumber.ReadPretty,decimal:r.InputNumber.ReadPretty,date:r.DatePicker.ReadPretty,string:r.Input.ReadPretty};function U(){const e=a.useFieldSchema(),t=r.useCollection_deprecated(),{getCollection:i,getCollectionField:o}=r.useCollectionManager_deprecated(),u=e.name.split(".");let c=t;for(let p=0;p<u.length-1;p++){const m=c.getField(u[p]);c=i(m.target)}return o(`${c.name}.${u[u.length-1]}`)}function z(e,t,i){const o=e[t];return Array.isArray(o)?o[i]:o&&typeof o=="object"?o:e}function H(e){var q,A,R;const j=e,{value:t}=j,i=$(j,["value"]),o=a.useFieldSchema(),{dataType:u,expression:c,engine:p="math.js"}=(q=U())!=null?q:{},[m,b]=N.useState(t),{evaluate:l}=g.evaluators.get(p),T=r.useFormBlockContext(),M=a.useField().path.entire,O=M==null?void 0:M.replace(`.${o.name}`,""),Z=O.split(".")[0],ee=parseInt((A=O.split("."))==null?void 0:A[1]);N.useEffect(()=>{b(t)},[t]),a.useFormEffects(()=>{a.onFormValuesChange(re=>{var k;if(o.name.indexOf(".")>=0||!(T!=null&&T.form)||(k=T.form)!=null&&k.readPretty)return;const ne=a.toJS(z(re.values,Z,ee));let f;try{f=l(c,ne),f=G(f,u)}catch(ie){f=null}f==null&&m==null&&b(f),b(f),f!==t&&e.onChange(f)})});const te=(R=J[u])!=null?R:r.Input;return s.jsx(te,y(h({},i),{value:u==="double"?g.toFixedByStep(m,e.step):m}))}const P=()=>null;P.Expression=v,P.Result=a.connect(H);const x="field-formula",K=[{dependencies:["dataType"],fulfill:{state:{display:'{{["double", "decimal"].includes($deps[0]) ? "visible" : "none"}}'}}}],D=[{dependencies:["dataType"],fulfill:{state:{display:'{{$deps[0] === "date" ? "visible" : "none"}}'}}}],{defaultProps:L,dateTimeProps:I,operators:Q}=r.interfacesProperties,W={"uiSchema.x-component-props.dateFormat":y(h({},S.cloneDeep(I["uiSchema.x-component-props.dateFormat"])),{"x-reactions":D}),"uiSchema.x-component-props.showTime":y(h({},S.cloneDeep(I["uiSchema.x-component-props.showTime"])),{"x-reactions":[...I["uiSchema.x-component-props.showTime"]["x-reactions"],...D]}),"uiSchema.x-component-props.timeFormat":h({},S.cloneDeep(I["uiSchema.x-component-props.timeFormat"]))};class X extends r.CollectionFieldInterface{constructor(){super(...arguments),this.name="formula",this.type="object",this.group="advanced",this.order=1,this.title=`{{t("Formula", { ns: "${x}" })}}`,this.icon="FunctionOutlined",this.description=`{{t("Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.", { ns: "${x}" })}}`,this.sortable=!0,this.default={type:"formula",uiSchema:{type:"string","x-component":"Formula.Result","x-component-props":{stringMode:!0,step:"1"},"x-read-pretty":!0}},this.properties=y(h(y(h({},L),{dataType:{type:"string",title:'{{t("Storage type")}}',"x-decorator":"FormItem","x-component":"Select","x-disabled":"{{ !createOnly }}",enum:[{value:"boolean",label:"Boolean"},{value:"integer",label:"Integer"},{value:"bigInt",label:"Big integer"},{value:"double",label:"Double"},{value:"string",label:"String"},{value:"date",label:"Datetime"}],required:!0,default:"double"},"uiSchema.x-component-props.addonAfter":{type:"string",title:'{{t("Suffix")}}',"x-decorator":"FormItem","x-component":"Input"},"uiSchema.x-component-props.step":{type:"string",title:'{{t("Precision")}}',"x-component":"Select","x-decorator":"FormItem",required:!0,default:"0",enum:[{value:"0",label:"1"},{value:"0.1",label:"1.0"},{value:"0.01",label:"1.00"},{value:"0.001",label:"1.000"},{value:"0.0001",label:"1.0000"},{value:"0.00001",label:"1.00000"},{value:"0.000001",label:"1.000000"},{value:"0.0000001",label:"1.0000000"},{value:"0.00000001",label:"1.00000000"},{value:"0.000000001",label:"1.000000000"},{value:"0.0000000001",label:"1.0000000000"}],"x-reactions":K}}),W),{engine:{type:"string",title:`{{t("Calculation engine", { ns: "${x}" })}}`,"x-decorator":"FormItem","x-component":"Radio.Group",enum:Array.from(g.evaluators.getEntities()).reduce((t,[i,o])=>t.concat(h({value:i},o)),[]),required:!0,default:"math.js"},expression:{type:"string",title:`{{t("Expression", { ns: "${x}" })}}`,required:!0,"x-component":"Formula.Expression","x-decorator":"FormItem","x-component-props":{supports:["checkbox","number","percent","integer","number","percent","sequence","input","textarea","email","phone","bankCard","datetime","createdAt","updatedAt","radioGroup","checkboxGroup","select","multipleSelect","m2o","o2m"],useCurrentFields:"{{ useCurrentFields }}"},"x-reactions":{dependencies:["engine"],fulfill:{schema:{description:"{{renderExpressionDescription($deps[0])}}"}}},"x-validator"(t,i,{form:o}){const{values:u}=o,{evaluate:c}=g.evaluators.get(u.engine),p=t.trim().replace(/{{\s*([^{}]+)\s*}}/g,"1");try{return c(p),""}catch(m){return r.i18n.t("Expression syntax error",{ns:x})}}}}),this.filterable={operators:Q.string},this.titleUsable=!0}}function Y(e){const t=g.evaluators.get(e);return t!=null&&t.link?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:r.css`
1
+ (function(n,r){typeof exports=="object"&&typeof module!="undefined"?r(exports,require("@tachybase/client"),require("@tachybase/schema"),require("react/jsx-runtime"),require("react"),require("@tego/client"),require("lodash"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","@tachybase/schema","react/jsx-runtime","react","@tego/client","lodash","react-i18next"],r):(n=typeof globalThis!="undefined"?globalThis:n||self,r(n["@tachybase/plugin-field-formula"]={},n["@tachybase/client"],n["@tachybase/schema"],n.jsxRuntime,n.react,n["@tego/client"],n.lodash))})(this,function(n,r,a,s,N,g,S){"use strict";var ae=Object.defineProperty,oe=Object.defineProperties;var ue=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var V=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable;var w=(n,r,a)=>r in n?ae(n,r,{enumerable:!0,configurable:!0,writable:!0,value:a}):n[r]=a,h=(n,r)=>{for(var a in r||(r={}))V.call(r,a)&&w(n,a,r[a]);if(C)for(var a of C(r))_.call(r,a)&&w(n,a,r[a]);return n},y=(n,r)=>oe(n,ue(r));var $=(n,r)=>{var a={};for(var s in n)V.call(n,s)&&r.indexOf(s)<0&&(a[s]=n[s]);if(n!=null&&C)for(var s of C(n))r.indexOf(s)<0&&_.call(n,s)&&(a[s]=n[s]);return a};var B=(n,r,a)=>new Promise((s,N)=>{var g=d=>{try{v(a.next(d))}catch(F){N(F)}},S=d=>{try{v(a.throw(d))}catch(F){N(F)}},v=d=>d.done?s(d.value):Promise.resolve(d.value).then(g,S);v((a=a.apply(n,r)).next())});const v=e=>{var b;const{value:t="",supports:i=[],useCurrentFields:o,onChange:u}=e,c=r.useCompile(),{getCollectionFields:p}=r.useCollectionManager_deprecated(),m=d(c,p,(b=o==null?void 0:o())!=null?b:[]);return s.jsx(r.Variable.TextArea,{value:t,onChange:u,scope:m})},d=(e,t,i)=>{const o=[];return i==null||i.forEach(u=>{var c,p,m,b;!["m2m","o2m"].includes(u.interface)&&["hasOne","hasMany","belongsTo","belongsToMany"].includes(u.type)&&u.uiSchema&&o.push({label:e(u.uiSchema.title),value:u.name,children:(p=(c=t(u.target))==null?void 0:c.filter(l=>l.uiSchema).map(l=>({label:l.uiSchema?e(l.uiSchema.title):"",value:l.name})))!=null?p:[]}),["hasOne","hasMany","belongsTo","belongsToMany"].includes(u.type)||o.push({label:e(u.uiSchema.title),value:u.name,children:(b=(m=t(u.target))==null?void 0:m.filter(l=>l.uiSchema).map(l=>({label:l.uiSchema?e(l.uiSchema.title):"",value:l.name})))!=null?b:[]})}),o},F={boolean:Boolean,integer:{boolean(e){return Number(e)},number(e){return e>=0?Math.floor(e):Math.ceil(e)},bigint(e){return Number(e)},string(e){const t=Number.parseInt(e,10);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},bigInt:{boolean(e){return Number(e)},number(e){return Math.floor(e>=0?Math.floor(e):Math.ceil(e))},bigint(e){return Number(e)},string(e){const t=Number.parseInt(e,10);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},double:{boolean(e){return Number(e)},number(e){return e},bigint(e){return Number(e)},string(e){const t=Number.parseFloat(e);return Number.isNaN(t)||!Number.isFinite(t)?null:t},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},decimal:{boolean(e){return Number(e)},number(e){return e},bigint(e){return e},date(e){const t=e.valueOf();return Number.isNaN(t)?null:t}},string:{boolean(e){return e.toString()},number(e){return e.toString()},bigint(e){return e.toString()},string(e){return e},date(e){return e.toISOString()}},date:{boolean(e){return null},number(e){const t=new Date(e);return Number.isNaN(t.valueOf())?null:t},bigint(e){const t=new Date(Number(e));return Number.isNaN(t.valueOf())?null:t},string(e){const t=Date.parse(e);return Number.isNaN(t)?null:new Date(t)},date(e){return new Date(e)}}};function G(e,t){if(e==null)return null;let i=typeof e;i==="object"&&e instanceof Date&&(i="date");const o=F[t];if(!o)return null;if(typeof o=="function")return o(e);const u=o[i];return u?u(e):null}const J={boolean:r.Checkbox.ReadPretty,integer:r.InputNumber.ReadPretty,bigInt:r.InputNumber.ReadPretty,double:r.InputNumber.ReadPretty,decimal:r.InputNumber.ReadPretty,date:r.DatePicker.ReadPretty,string:r.Input.ReadPretty};function U(){const e=a.useFieldSchema(),t=r.useCollection_deprecated(),{getCollection:i,getCollectionField:o}=r.useCollectionManager_deprecated(),u=e.name.split(".");let c=t;for(let p=0;p<u.length-1;p++){const m=c.getField(u[p]);c=i(m.target)}return o(`${c.name}.${u[u.length-1]}`)}function z(e,t,i){const o=e[t];return Array.isArray(o)?o[i]:o&&typeof o=="object"?o:e}function H(e){var q,A,R;const j=e,{value:t}=j,i=$(j,["value"]),o=a.useFieldSchema(),{dataType:u,expression:c,engine:p="math.js"}=(q=U())!=null?q:{},[m,b]=N.useState(t),{evaluate:l}=g.evaluators.get(p),T=r.useFormBlockContext(),M=a.useField().path.entire,O=M==null?void 0:M.replace(`.${o.name}`,""),Z=O.split(".")[0],ee=parseInt((A=O.split("."))==null?void 0:A[1]);N.useEffect(()=>{b(t)},[t]),a.useFormEffects(()=>{a.onFormValuesChange(re=>{var k;if(o.name.indexOf(".")>=0||!(T!=null&&T.form)||(k=T.form)!=null&&k.readPretty)return;const ne=a.toJS(z(re.values,Z,ee));let f;try{f=l(c,ne),f=G(f,u)}catch(ie){f=null}f==null&&m==null&&b(f),b(f),f!==t&&e.onChange(f)})});const te=(R=J[u])!=null?R:r.Input;return s.jsx(te,y(h({},i),{value:u==="double"?g.toFixedByStep(m,e.step):m}))}const P=()=>null;P.Expression=v,P.Result=a.connect(H);const x="field-formula",K=[{dependencies:["dataType"],fulfill:{state:{display:'{{["double", "decimal"].includes($deps[0]) ? "visible" : "none"}}'}}}],D=[{dependencies:["dataType"],fulfill:{state:{display:'{{$deps[0] === "date" ? "visible" : "none"}}'}}}],{defaultProps:L,dateTimeProps:I,operators:Q}=r.interfacesProperties,W={"uiSchema.x-component-props.dateFormat":y(h({},S.cloneDeep(I["uiSchema.x-component-props.dateFormat"])),{"x-reactions":D}),"uiSchema.x-component-props.showTime":y(h({},S.cloneDeep(I["uiSchema.x-component-props.showTime"])),{"x-reactions":[...I["uiSchema.x-component-props.showTime"]["x-reactions"],...D]}),"uiSchema.x-component-props.timeFormat":h({},S.cloneDeep(I["uiSchema.x-component-props.timeFormat"]))};class X extends r.CollectionFieldInterface{constructor(){super(...arguments),this.name="formula",this.type="object",this.group="advanced",this.order=1,this.title=`{{t("Formula", { ns: "${x}" })}}`,this.icon="FunctionOutlined",this.description=`{{t("Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.", { ns: "${x}" })}}`,this.sortable=!0,this.default={type:"formula",uiSchema:{type:"string","x-component":"Formula.Result","x-component-props":{stringMode:!0,step:"1"},"x-read-pretty":!0}},this.properties=y(h(y(h({},L),{dataType:{type:"string",title:'{{t("Storage type")}}',"x-decorator":"FormItem","x-component":"Select","x-disabled":"{{ !createOnly }}",enum:[{value:"boolean",label:"Boolean"},{value:"integer",label:"Integer"},{value:"bigInt",label:"Big integer"},{value:"double",label:"Double"},{value:"string",label:"String"},{value:"date",label:"Datetime"}],required:!0,default:"double"},"uiSchema.x-component-props.addonAfter":{type:"string",title:'{{t("Suffix")}}',"x-decorator":"FormItem","x-component":"Input"},"uiSchema.x-component-props.step":{type:"string",title:'{{t("Precision")}}',"x-component":"Select","x-decorator":"FormItem",required:!0,default:"0",enum:[{value:"0",label:"1"},{value:"0.1",label:"1.0"},{value:"0.01",label:"1.00"},{value:"0.001",label:"1.000"},{value:"0.0001",label:"1.0000"},{value:"0.00001",label:"1.00000"},{value:"0.000001",label:"1.000000"},{value:"0.0000001",label:"1.0000000"},{value:"0.00000001",label:"1.00000000"},{value:"0.000000001",label:"1.000000000"},{value:"0.0000000001",label:"1.0000000000"}],"x-reactions":K}}),W),{engine:{type:"string",title:`{{t("Calculation engine", { ns: "${x}" })}}`,"x-decorator":"FormItem","x-component":"Radio.Group",enum:Array.from(g.evaluators.getEntities()).reduce((t,[i,o])=>t.concat(h({value:i},o)),[]),required:!0,default:"math.js"},expression:{type:"string",title:`{{t("Expression", { ns: "${x}" })}}`,required:!0,"x-component":"Formula.Expression","x-decorator":"FormItem","x-component-props":{supports:["checkbox","number","percent","integer","number","percent","sequence","input","textarea","email","phone","bankCard","datetime","createdAt","updatedAt","radioGroup","checkboxGroup","select","multipleSelect","m2o","o2m"],useCurrentFields:"{{ useCurrentFields }}"},"x-reactions":{dependencies:["engine"],fulfill:{schema:{description:"{{renderExpressionDescription($deps[0])}}"}}},"x-validator"(t,i,{form:o}){const{values:u}=o,{evaluate:c}=g.evaluators.get(u.engine),p=t.trim().replace(/{{\s*([^{}]+)\s*}}/g,"1");try{return c(p),""}catch(m){return r.i18n.t("Expression syntax error",{ns:x})}}}}),this.filterable={operators:Q.string},this.titleUsable=!0}}function Y(e){const t=g.evaluators.get(e);return t!=null&&t.link?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:r.css`
2
2
  &:after {
3
3
  content: ':';
4
4
  }
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- "@tachybase/client": "1.6.23",
2
+ "@tachybase/client": "1.6.24",
3
3
  "react": "18.3.1",
4
4
  "@tego/client": "1.6.16",
5
5
  "@tego/server": "1.6.16",
@@ -7,13 +7,8 @@ export interface FormulaFieldOptions extends BaseFieldOptions {
7
7
  export declare class FormulaField extends Field {
8
8
  get dataType(): any;
9
9
  calculate(scope: any): any;
10
- initFieldData: ({ transaction }: {
11
- transaction: any;
12
- }) => Promise<void>;
13
- calculateField: (instance: any) => Promise<void>;
14
- updateFieldData: (instance: any, { transaction }: {
15
- transaction: any;
16
- }) => Promise<void>;
10
+ getCalculationScope: (instance: any, options?: any) => Promise<any>;
11
+ calculateField: (instance: any, options?: any) => Promise<void>;
17
12
  bind(): void;
18
13
  unbind(): void;
19
14
  }
@@ -34,76 +34,61 @@ const DataTypeMap = {
34
34
  class FormulaField extends import_server.Field {
35
35
  constructor() {
36
36
  super(...arguments);
37
- this.initFieldData = async ({ transaction }) => {
38
- const { name } = this.options;
39
- const records = await this.collection.repository.find({
40
- order: [this.collection.model.primaryKeyAttribute],
41
- transaction
42
- });
43
- for (const record of records) {
44
- const scope = record.toJSON();
45
- const result = this.calculate(scope);
46
- if (result != null) {
47
- await record.update(
48
- {
49
- [name]: result
50
- },
51
- {
52
- transaction,
53
- silent: true,
54
- hooks: false
55
- }
56
- );
37
+ this.getCalculationScope = async (instance, options) => {
38
+ var _a, _b, _c, _d;
39
+ const appends = (0, import_utils.getAppendsFromExpression)(this.options.expression);
40
+ const instanceScope = instance.toJSON();
41
+ let scope = { ...instanceScope };
42
+ if (!appends.length) {
43
+ return scope;
44
+ }
45
+ const pk = this.collection.model.primaryKeyAttribute;
46
+ const pkValue = instance.get(pk) ?? instanceScope[pk];
47
+ if (pkValue) {
48
+ const record = await this.collection.repository.findOne({
49
+ filterByTk: pkValue,
50
+ transaction: options == null ? void 0 : options.transaction
51
+ });
52
+ if (record) {
53
+ scope = { ...record.toJSON(), ...instanceScope };
54
+ }
55
+ }
56
+ for (const append of appends) {
57
+ const field = this.collection.getField(append);
58
+ const target = (field == null ? void 0 : field.target) ?? ((_a = field == null ? void 0 : field.options) == null ? void 0 : _a.target);
59
+ if (!target) {
60
+ continue;
61
+ }
62
+ const fkField = ((_b = field.options) == null ? void 0 : _b.foreignKey) ?? field.foreignKey ?? `${append}Id`;
63
+ const fkValue = scope[fkField];
64
+ if (fkValue == null) {
65
+ continue;
66
+ }
67
+ const targetKey = ((_c = field.options) == null ? void 0 : _c.targetKey) ?? "id";
68
+ const relatedId = (_d = scope[append]) == null ? void 0 : _d[targetKey];
69
+ if (relatedId != null && `${relatedId}` === `${fkValue}`) {
70
+ continue;
71
+ }
72
+ const targetCollection = this.database.getCollection(target);
73
+ if (!targetCollection) {
74
+ continue;
75
+ }
76
+ const targetRecord = await targetCollection.repository.findOne({
77
+ filterByTk: fkValue,
78
+ transaction: options == null ? void 0 : options.transaction
79
+ });
80
+ if (targetRecord) {
81
+ scope[append] = targetRecord.toJSON();
57
82
  }
58
83
  }
84
+ return scope;
59
85
  };
60
- this.calculateField = async (instance) => {
86
+ this.calculateField = async (instance, options) => {
61
87
  const { name } = this.options;
62
- const result = this.calculate(instance.toJSON());
88
+ const scope = await this.getCalculationScope(instance, options);
89
+ const result = this.calculate(scope);
63
90
  instance.set(name, result);
64
91
  };
65
- this.updateFieldData = async (instance, { transaction }) => {
66
- if (this.collection.name !== instance.collectionName || instance.name !== this.options.name) {
67
- return;
68
- }
69
- this.options = Object.assign(this.options, instance.options);
70
- const { name } = this.options;
71
- const regex = /\{\{([^}]+)\}\}/g;
72
- const variables = [];
73
- let match;
74
- while ((match = regex.exec(this.options.expression)) !== null) {
75
- variables.push(match[1]);
76
- }
77
- const uniqueVariables = [...new Set(variables)];
78
- const appends = uniqueVariables.map((item) => {
79
- if (item.includes(".")) {
80
- const parts = item.split(".");
81
- parts.pop();
82
- const result = parts.join(".");
83
- return result;
84
- }
85
- return "";
86
- }).filter(Boolean);
87
- const records = await this.collection.repository.find({
88
- order: [this.collection.model.primaryKeyAttribute],
89
- transaction,
90
- appends
91
- });
92
- for (const record of records) {
93
- const scope = record.toJSON();
94
- const result = this.calculate(scope);
95
- await record.update(
96
- {
97
- [name]: result
98
- },
99
- {
100
- transaction,
101
- silent: true,
102
- hooks: false
103
- }
104
- );
105
- }
106
- };
107
92
  }
108
93
  get dataType() {
109
94
  const { dataType } = this.options;
@@ -112,6 +97,10 @@ class FormulaField extends import_server.Field {
112
97
  calculate(scope) {
113
98
  const { expression, engine = "math.js", dataType = "double" } = this.options;
114
99
  const evaluate = import_server.evaluators.get(engine);
100
+ if (!evaluate) {
101
+ console.error(`Formula evaluator "${engine}" is not registered.`);
102
+ return null;
103
+ }
115
104
  try {
116
105
  const result = evaluate(expression, scope);
117
106
  return (0, import_utils.toDbType)(result, dataType);
@@ -122,13 +111,11 @@ class FormulaField extends import_server.Field {
122
111
  }
123
112
  bind() {
124
113
  super.bind();
125
- this.database.on("fields.afterUpdate", this.updateFieldData);
126
114
  this.on("beforeSave", this.calculateField);
127
115
  }
128
116
  unbind() {
129
117
  super.unbind();
130
118
  this.off("beforeSave", this.calculateField);
131
- this.database.off("fields.afterUpdate", this.updateFieldData);
132
119
  }
133
120
  }
134
121
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,3 +1,4 @@
1
+ export declare function getAppendsFromExpression(expression: string): string[];
1
2
  export declare const DataTypeTransformers: {
2
3
  boolean: BooleanConstructor;
3
4
  integer: {
@@ -18,9 +18,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var utils_exports = {};
19
19
  __export(utils_exports, {
20
20
  DataTypeTransformers: () => DataTypeTransformers,
21
+ getAppendsFromExpression: () => getAppendsFromExpression,
21
22
  toDbType: () => toDbType
22
23
  });
23
24
  module.exports = __toCommonJS(utils_exports);
25
+ function getAppendsFromExpression(expression) {
26
+ const regex = /\{\{([^}]+)\}\}/g;
27
+ const variables = [];
28
+ let match;
29
+ while ((match = regex.exec(expression)) !== null) {
30
+ variables.push(match[1].trim());
31
+ }
32
+ const uniqueVariables = [...new Set(variables)];
33
+ return uniqueVariables.map((item) => {
34
+ if (item.includes(".")) {
35
+ const parts = item.split(".");
36
+ parts.pop();
37
+ return parts.join(".");
38
+ }
39
+ return "";
40
+ }).filter(Boolean);
41
+ }
24
42
  const DataTypeTransformers = {
25
43
  boolean: Boolean,
26
44
  integer: {
@@ -168,7 +186,7 @@ function toDbType(value, type) {
168
186
  return null;
169
187
  }
170
188
  let jsType = typeof value;
171
- if (jsType == "object" && value instanceof Date) {
189
+ if (jsType === "object" && value instanceof Date) {
172
190
  jsType = "date";
173
191
  }
174
192
  const transformers = DataTypeTransformers[type];
@@ -184,5 +202,6 @@ function toDbType(value, type) {
184
202
  // Annotate the CommonJS export names for ESM import in node:
185
203
  0 && (module.exports = {
186
204
  DataTypeTransformers,
205
+ getAppendsFromExpression,
187
206
  toDbType
188
207
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tachybase/plugin-field-formula",
3
3
  "displayName": "Field: Formula",
4
- "version": "1.6.23",
4
+ "version": "1.6.24",
5
5
  "description": "Configure and store the results of calculations between multiple field values in the same record, supporting both Math.js and Excel formula functions.",
6
6
  "keywords": [
7
7
  "Collection fields"
@@ -26,7 +26,7 @@
26
26
  "lodash": "^4.17.21",
27
27
  "react": "18.3.1",
28
28
  "react-i18next": "16.2.1",
29
- "@tachybase/client": "1.6.23"
29
+ "@tachybase/client": "1.6.24"
30
30
  },
31
31
  "description.zh-CN": "可以配置并存储同一条记录的多字段值之间的计算结果,支持 Math.js 和 Excel formula functions 两种引擎",
32
32
  "displayName.zh-CN": "数据表字段:公式"