amis 1.9.1-beta.11 → 1.9.1-beta.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/lib/RootRenderer.js +14 -0
  2. package/lib/RootRenderer.js.map +2 -2
  3. package/lib/SchemaRenderer.js +8 -3
  4. package/lib/SchemaRenderer.js.map +2 -2
  5. package/lib/components/Alert2.d.ts +10 -10
  6. package/lib/components/DateRangePicker.js +11 -9
  7. package/lib/components/DateRangePicker.js.map +2 -2
  8. package/lib/components/SearchBox.d.ts +40 -40
  9. package/lib/components/Select.d.ts +193 -193
  10. package/lib/components/Select.js +2 -4
  11. package/lib/components/Select.js.map +2 -2
  12. package/lib/components/virtual-list/index.js +1 -1
  13. package/lib/components/virtual-list/index.js.map +1 -1
  14. package/lib/index.js +1 -1
  15. package/lib/locale/de-DE.js +1 -0
  16. package/lib/locale/de-DE.js.map +2 -2
  17. package/lib/locale/en-US.js +1 -0
  18. package/lib/locale/en-US.js.map +2 -2
  19. package/lib/locale/zh-CN.js +5 -2
  20. package/lib/locale/zh-CN.js.map +2 -2
  21. package/lib/renderers/Action.d.ts +6 -0
  22. package/lib/renderers/Action.js.map +2 -2
  23. package/lib/renderers/CRUD.js +2 -1
  24. package/lib/renderers/CRUD.js.map +2 -2
  25. package/lib/renderers/Card.js +5 -4
  26. package/lib/renderers/Card.js.map +2 -2
  27. package/lib/renderers/Carousel.d.ts +1 -1
  28. package/lib/renderers/Carousel.js +5 -1
  29. package/lib/renderers/Carousel.js.map +2 -2
  30. package/lib/renderers/Form/InputImage.js +1 -1
  31. package/lib/renderers/Form/InputImage.js.map +2 -2
  32. package/lib/renderers/Form/InputText.js +1 -1
  33. package/lib/renderers/Form/InputText.js.map +2 -2
  34. package/lib/renderers/Form/Options.js +1 -0
  35. package/lib/renderers/Form/Options.js.map +2 -2
  36. package/lib/renderers/Form/Select.d.ts +12 -0
  37. package/lib/renderers/Form/Select.js +1 -3
  38. package/lib/renderers/Form/Select.js.map +2 -2
  39. package/lib/renderers/Form/wrapControl.js +72 -19
  40. package/lib/renderers/Form/wrapControl.js.map +2 -2
  41. package/lib/renderers/Service.js +1 -0
  42. package/lib/renderers/Service.js.map +2 -2
  43. package/lib/renderers/Table/TableContent.d.ts +2 -2
  44. package/lib/renderers/Table/TableContent.js +4 -16
  45. package/lib/renderers/Table/TableContent.js.map +2 -2
  46. package/lib/renderers/Table/index.d.ts +2 -5
  47. package/lib/renderers/Table/index.js +2 -2
  48. package/lib/renderers/Table/index.js.map +2 -2
  49. package/lib/store/table.js +7 -3
  50. package/lib/store/table.js.map +2 -2
  51. package/lib/themes/ang-ie11.css +0 -1
  52. package/lib/themes/ang.css +0 -1
  53. package/lib/themes/ang.css.map +1 -1
  54. package/lib/themes/antd-ie11.css +0 -1
  55. package/lib/themes/antd.css +0 -1
  56. package/lib/themes/antd.css.map +1 -1
  57. package/lib/themes/cxd-ie11.css +8 -1
  58. package/lib/themes/cxd.css +8 -1
  59. package/lib/themes/cxd.css.map +1 -1
  60. package/lib/themes/dark-ie11.css +0 -1
  61. package/lib/themes/dark.css +0 -1
  62. package/lib/themes/dark.css.map +1 -1
  63. package/lib/themes/default-ie11.css +8 -1
  64. package/lib/themes/default.css +8 -1
  65. package/lib/themes/default.css.map +1 -1
  66. package/lib/types.d.ts +1 -1
  67. package/lib/types.js.map +1 -1
  68. package/lib/utils/formula.d.ts +16 -0
  69. package/lib/utils/formula.js +207 -0
  70. package/lib/utils/formula.js.map +13 -0
  71. package/lib/utils/grammar.d.ts +4 -0
  72. package/lib/utils/grammar.js +47 -0
  73. package/lib/utils/grammar.js.map +13 -0
  74. package/lib/utils/helper.js +1 -0
  75. package/lib/utils/helper.js.map +2 -2
  76. package/lib/utils/tpl.js +3 -0
  77. package/lib/utils/tpl.js.map +2 -2
  78. package/package.json +1 -1
  79. package/schema.json +278 -222
  80. package/scss/components/_collapse.scss +1 -1
  81. package/scss/themes/cxd.scss +12 -0
  82. package/sdk/ang-ie11.css +0 -1
  83. package/sdk/ang.css +0 -1
  84. package/sdk/antd-ie11.css +0 -1
  85. package/sdk/antd.css +0 -1
  86. package/sdk/barcode.js +51 -51
  87. package/sdk/charts.js +14 -14
  88. package/sdk/codemirror.js +7 -7
  89. package/sdk/color-picker.js +65 -65
  90. package/sdk/cropperjs.js +2 -2
  91. package/sdk/cxd-ie11.css +9 -1
  92. package/sdk/cxd.css +9 -1
  93. package/sdk/dark-ie11.css +0 -1
  94. package/sdk/dark.css +0 -1
  95. package/sdk/exceljs.js +1 -1
  96. package/sdk/locale/de-DE.js +1 -0
  97. package/sdk/markdown.js +69 -69
  98. package/sdk/papaparse.js +1 -1
  99. package/sdk/renderers/Form/CityDB.js +1 -1
  100. package/sdk/rest.js +16 -16
  101. package/sdk/rich-text.js +62 -62
  102. package/sdk/sdk-ie11.css +9 -1
  103. package/sdk/sdk.css +9 -1
  104. package/sdk/sdk.js +1695 -1665
  105. package/sdk/thirds/hls.js/hls.js +1 -1
  106. package/sdk/thirds/mpegts.js/mpegts.js +1 -1
  107. package/sdk/tinymce.js +57 -57
  108. package/src/RootRenderer.tsx +14 -0
  109. package/src/SchemaRenderer.tsx +4 -2
  110. package/src/components/DateRangePicker.tsx +73 -60
  111. package/src/components/Select.tsx +2 -4
  112. package/src/components/virtual-list/index.tsx +1 -1
  113. package/src/locale/de-DE.ts +1 -0
  114. package/src/locale/en-US.ts +1 -0
  115. package/src/locale/zh-CN.ts +5 -2
  116. package/src/renderers/Action.tsx +8 -0
  117. package/src/renderers/CRUD.tsx +2 -1
  118. package/src/renderers/Card.tsx +10 -5
  119. package/src/renderers/Carousel.tsx +10 -4
  120. package/src/renderers/Form/InputImage.tsx +1 -1
  121. package/src/renderers/Form/InputText.tsx +1 -0
  122. package/src/renderers/Form/Options.tsx +1 -0
  123. package/src/renderers/Form/Select.tsx +13 -3
  124. package/src/renderers/Form/wrapControl.tsx +90 -22
  125. package/src/renderers/Service.tsx +1 -0
  126. package/src/renderers/Table/TableContent.tsx +22 -34
  127. package/src/renderers/Table/index.tsx +4 -8
  128. package/src/store/table.ts +10 -3
  129. package/src/types.ts +1 -0
  130. package/src/utils/formula.ts +240 -0
  131. package/src/utils/grammar.ts +53 -0
  132. package/src/utils/helper.ts +2 -1
  133. package/src/utils/tpl.ts +2 -0
@@ -7,8 +7,7 @@ import {LocaleProps} from '../../locale';
7
7
  import {observer} from 'mobx-react';
8
8
  import {ActionSchema} from '../Action';
9
9
  import ItemActionsWrapper from './ItemActionsWrapper';
10
- import {SchemaTpl, SchemaIcon} from '../../Schema';
11
- import {generateIcon} from '../../utils/icon';
10
+ import {SchemaTpl} from '../../Schema';
12
11
  import {Icon} from '../../components/icons';
13
12
  import {OnEventProps} from '../../utils/renderer-event';
14
13
 
@@ -26,7 +25,6 @@ export interface TableContentProps extends LocaleProps {
26
25
  }>;
27
26
  rows: Array<IRow>;
28
27
  placeholder?: string | SchemaTpl;
29
- emptyIcon?: SchemaIcon;
30
28
  render: (region: string, node: SchemaNode, props?: any) => JSX.Element;
31
29
  onMouseMove: (event: React.MouseEvent) => void;
32
30
  onScroll: (event: React.UIEvent) => void;
@@ -61,7 +59,8 @@ export interface TableContentProps extends LocaleProps {
61
59
  itemActions?: Array<Action>;
62
60
  store: ITableStore;
63
61
  dispatchEvent?: Function;
64
- onEvent?: OnEventProps
62
+ onEvent?: OnEventProps;
63
+ loading?: boolean;
65
64
  }
66
65
 
67
66
  @observer
@@ -131,22 +130,14 @@ export class TableContent extends React.Component<TableContentProps> {
131
130
  itemAction,
132
131
  affixRow,
133
132
  store,
134
- emptyIcon,
135
133
  dispatchEvent,
136
- onEvent
134
+ onEvent,
135
+ loading
137
136
  } = this.props;
138
137
 
139
138
  const tableClassName = cx('Table-table', this.props.tableClassName);
140
139
  const hideHeader = columns.every(column => !column.label);
141
140
 
142
- let iconElement = null;
143
- if (emptyIcon) {
144
- iconElement =
145
- typeof emptyIcon === 'string'
146
- ? generateIcon(cx, emptyIcon, 'Icon')
147
- : render('icon', emptyIcon);
148
- }
149
-
150
141
  return (
151
142
  <div
152
143
  onMouseMove={onMouseMove}
@@ -185,26 +176,23 @@ export class TableContent extends React.Component<TableContentProps> {
185
176
  {!rows.length ? (
186
177
  <tbody>
187
178
  <tr className={cx('Table-placeholder')}>
188
- <td colSpan={columns.length}>
189
- {iconElement ? (
190
- React.cloneElement(iconElement, {
191
- className: cx(
192
- iconElement?.props?.className ?? '',
193
- 'Table-placeholder-empty-icon',
194
- 'icon'
195
- )
196
- })
197
- ) : (
198
- <Icon
199
- icon="desk-empty"
200
- className={cx('Table-placeholder-empty-icon', 'icon')}
201
- />
202
- )}
203
- {render(
204
- 'placeholder',
205
- translate(placeholder || 'placeholder.noData')
206
- )}
207
- </td>
179
+ {
180
+ !loading ? (
181
+ <td colSpan={columns.length}>
182
+ {
183
+ typeof placeholder === 'string' ? (
184
+ <>
185
+ <Icon
186
+ icon="desk-empty"
187
+ className={cx('Table-placeholder-empty-icon', 'icon')}
188
+ />
189
+ {translate(placeholder || 'placeholder.noData')}
190
+ </>
191
+ ) : render('placeholder', translate(placeholder || 'placeholder.noData'))
192
+ }
193
+ </td>
194
+ ) : null
195
+ }
208
196
  </tr>
209
197
  </tbody>
210
198
  ) : (
@@ -223,11 +223,6 @@ export interface TableSchema extends BaseSchema {
223
223
  */
224
224
  placeholder?: string | SchemaTpl;
225
225
 
226
- /**
227
- * 无数据展示 icon
228
- */
229
- emptyIcon?: string | SchemaIcon;
230
-
231
226
  /**
232
227
  * 是否显示底部
233
228
  */
@@ -365,6 +360,7 @@ export interface TableProps extends RendererProps {
365
360
  canAccessSuperData?: boolean;
366
361
  reUseRow?: boolean;
367
362
  itemBadge?: BadgeSchema;
363
+ loading?: boolean;
368
364
  }
369
365
 
370
366
  export type ExportExcelToolbar = SchemaNode & {
@@ -2638,9 +2634,9 @@ export default class Table extends React.Component<TableProps, object> {
2638
2634
  prefixRowClassName,
2639
2635
  autoFillHeight,
2640
2636
  itemActions,
2641
- emptyIcon,
2642
2637
  dispatchEvent,
2643
- onEvent
2638
+ onEvent,
2639
+ loading
2644
2640
  } = this.props;
2645
2641
 
2646
2642
  // 理论上来说 store.rows 应该也行啊
@@ -2663,7 +2659,6 @@ export default class Table extends React.Component<TableProps, object> {
2663
2659
  columnsGroup={store.columnGroup}
2664
2660
  rows={store.rows}
2665
2661
  placeholder={placeholder}
2666
- emptyIcon={emptyIcon}
2667
2662
  render={render}
2668
2663
  onMouseMove={this.handleMouseMove}
2669
2664
  onScroll={this.handleOutterScroll}
@@ -2688,6 +2683,7 @@ export default class Table extends React.Component<TableProps, object> {
2688
2683
  translate={translate}
2689
2684
  dispatchEvent={dispatchEvent}
2690
2685
  onEvent={onEvent}
2686
+ loading={loading}
2691
2687
  />
2692
2688
  );
2693
2689
  }
@@ -506,20 +506,27 @@ export const TableStore = iRendererStore
506
506
  groups[0].label = columns[1].groupName;
507
507
  }
508
508
 
509
+ // 用户是否启用了 groupName
510
+ const hasGroupName = columns.some(column => column.groupName);
511
+
509
512
  for (let i = 1; i < len; i++) {
510
513
  let prev = groups[groups.length - 1];
511
514
  const current = columns[i];
512
515
 
513
- if (
516
+ const groupNameIsSame =
514
517
  current.groupName === prev.label ||
515
518
  resolveVariableAndFilter(current.groupName, self.data) ===
516
- resolveVariableAndFilter(prev.label, self.data)
519
+ resolveVariableAndFilter(prev.label, self.data);
520
+
521
+ if (
522
+ groupNameIsSame &&
523
+ ((hasGroupName && current.groupName) || !hasGroupName)
517
524
  ) {
518
525
  prev.colSpan++;
519
526
  prev.has.push(current);
520
527
  } else {
521
528
  groups.push({
522
- label: current.groupName || ' ', // 如果中间没有配置groupName,那么样式会错乱,这里加一个空白字符,当做一个占位表头
529
+ label: current.groupName || current.label || ' ', // 如果中间没有配置groupName,那么样式会错乱,这里设置列的label配置,lable也没有则设置一个空字符串
523
530
  colSpan: 1,
524
531
  rowSpan: 1,
525
532
  index: current.index,
package/src/types.ts CHANGED
@@ -85,6 +85,7 @@ export interface Action extends Button {
85
85
  | 'copy'
86
86
  | 'reload'
87
87
  | 'ajax'
88
+ | 'saveAs'
88
89
  | 'dialog'
89
90
  | 'drawer'
90
91
  | 'jump'
@@ -0,0 +1,240 @@
1
+ import isObjectByLodash from 'lodash/isObject';
2
+ import isString from 'lodash/isString';
3
+ import isBoolean from 'lodash/isBoolean';
4
+ import {
5
+ getVariable,
6
+ isPureVariable,
7
+ resolveVariable,
8
+ resolveVariableAndFilter,
9
+ evaluate
10
+ } from 'amis-formula';
11
+
12
+ import {filter} from './tpl';
13
+ import {getFilters} from './tpl-builtin';
14
+ import {collectVariables} from './grammar';
15
+
16
+ /**
17
+ * formulaExec 运算器:根据当前字符串类型执行对应运算,也可按指定执行模式执行运算
18
+ *
19
+ * 运算模式(execMode)支持以下取值:
20
+ * 1. tpl: 按模板字符串执行(JavaScript 模板引擎),比如:Hello ${amisUser.email}、<h1>Hello</h1>, <span>${amisUser.email}</span>;
21
+ * 备注: 在模板中可以自由访问变量,详细请见:https://www.lodashjs.com/docs/lodash.template;
22
+ * 2. formula: 按新版公式表达式执行,用于执行 ${ xxx } 格式的表达式;
23
+ * 支持从window、localStorage、sessionStorage获取数据,比如:${num1 + 2}、${ls:env}、${window:document}、${window:document.URL}、${amisUser.email};
24
+ * 详细请见:https://aisuda.bce.baidu.com/amis/zh-CN/docs/concepts/data-mapping#namespace
25
+ * 3. evalFormula: 按新版公式表达式执行,用于执行 ${ xxx } 和 非${ xxx } 格式的表达式(evalMode 为 true,不用 ${} 包裹也可以执行),功能同 formula 运算模式;
26
+ * 4. js: 按Javascript执行,表达式中可以通过data.xxx来获取指定数据,并且支持简单运算;
27
+ * 比如:data.num1 + 2、this.num1 + 2、num1 + 2;(备注:三个表达式是等价的,这里的 this 就是 data。)
28
+ * 5. var: 以此字符串作为key值从当前数据域data中获取数值;性能最高(运行期间不会生成ast和表达式运算);
29
+ * 6. true 或者 false: 当execMode设置为true时,不用 ${} 包裹也可以执行表达式;
30
+ * 7. collect: 用于从表达式中获取所有变量;
31
+ *
32
+ * 备注1: 用户也可以使用 registerFormulaExec 注册一个自定义运算器;
33
+ * 备注2: 模板字符串 和 Javascript 模板引擎 不可以交叉使用;
34
+ * 备注3: amis 现有的 evalFormula 方法,可执行 ${} 格式类表达式,但不支持 filter 过滤器,所以这里用 resolveValueByName 实现;
35
+ * 备注4: 后续可考虑将 amis现有的运算器都放这里管理,充当统一的运算器入口。
36
+ */
37
+
38
+ // 缓存,用于提升性能
39
+ const FORMULA_EVAL_CACHE: {[key: string]: Function} = {};
40
+
41
+ /**
42
+ * 用于存储当前可用运算器,默认支持 tpl、formula、js、var 四种类型运算器
43
+ * 备注:在这里统一参数。
44
+ */
45
+ export const FormulaExec: {
46
+ [key: string]: Function;
47
+ } = {
48
+ tpl: (expression: string, data?: object) => {
49
+ const curData = data || {};
50
+ return filter(expression, curData);
51
+ },
52
+ formula: (expression: string, data?: object) => {
53
+ // 邮箱格式直接返回,后续需要在 amis-formula 中处理
54
+ if (
55
+ /^\$\{([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})\}$/.test(
56
+ expression
57
+ )
58
+ ) {
59
+ return expression.substring(2, expression.length - 1); // 剔除前后特殊字符
60
+ }
61
+ const curData = data || {};
62
+ let result = undefined;
63
+ try {
64
+ // 执行 ${} 格式类表达式,且支持 filter 过滤器。(备注: isPureVariable 可用于判断是否有 过滤器。)
65
+ result = resolveVariableAndFilter(expression, curData, '| raw');
66
+ } catch (e) {
67
+ console.warn(
68
+ '[formula]表达式执行异常,当前表达式: ',
69
+ expression,
70
+ ',当前上下文数据: ',
71
+ data
72
+ );
73
+ return expression;
74
+ }
75
+ // 备注: 此处不用 result ?? expression 是为了避免 没有对应结果时直接显示 expression: ${xxx}
76
+ return result;
77
+ },
78
+ evalFormula: (expression: string, data?: object) => {
79
+ const curData = data || {};
80
+ let result = undefined;
81
+ try {
82
+ result = evaluate(expression, curData, {
83
+ evalMode: true, // evalMode 为 true 时,不用 ${} 包裹也可以执行,
84
+ allowFilter: false
85
+ });
86
+ } catch (e) {
87
+ console.warn(
88
+ '[evalFormula]表达式执行异常,当前表达式: ',
89
+ expression,
90
+ ',当前上下文数据: ',
91
+ data
92
+ );
93
+ return expression;
94
+ }
95
+ return result ?? expression;
96
+ },
97
+ js: (expression: string, data?: object) => {
98
+ let debug = false;
99
+ const idx = expression.indexOf('debugger');
100
+ if (~idx) {
101
+ debug = true;
102
+ expression = expression.replace(/debugger;?/, '');
103
+ }
104
+
105
+ let fn;
106
+ if (expression in FORMULA_EVAL_CACHE) {
107
+ fn = FORMULA_EVAL_CACHE[expression];
108
+ } else {
109
+ fn = new Function(
110
+ 'data',
111
+ 'utils',
112
+ `with(data) {${debug ? 'debugger;' : ''}return (${expression});}`
113
+ );
114
+ FORMULA_EVAL_CACHE[expression] = fn;
115
+ }
116
+
117
+ data = data || {};
118
+
119
+ let curResult = undefined;
120
+ try {
121
+ curResult = fn.call(data, data, getFilters());
122
+ } catch (e) {
123
+ console.warn(
124
+ '[formula:js]表达式执行异常,当前表达式: ',
125
+ expression,
126
+ ',当前上下文数据: ',
127
+ data
128
+ );
129
+ return expression;
130
+ }
131
+ return curResult;
132
+ },
133
+ var: (expression: string, data?: object) => {
134
+ const curData = data || {};
135
+ const result = getVariable(curData, expression); // 不支持过滤器
136
+ return result ?? expression;
137
+ },
138
+ collect: (expression: any) => {
139
+ let variables: Array<string> = [];
140
+ if (isObjectByLodash(expression) || isString(expression)) {
141
+ // 仅对 Object类型 和 String类型 进行变量提取
142
+ variables = collectVariables(expression);
143
+ } else {
144
+ variables = [];
145
+ }
146
+ return variables;
147
+ }
148
+ };
149
+
150
+ // 根据表达式类型自动匹配指定运算器,也可以通过设置 execMode 直接指定运算器
151
+ export function formulaExec(
152
+ value: any,
153
+ data: any,
154
+ execMode?: string | boolean
155
+ ) {
156
+ if (!value) {
157
+ return '';
158
+ }
159
+ let OpenFormulaExecEvalMode = false;
160
+ let curExecMode = '';
161
+ if (isBoolean(execMode)) {
162
+ // OpenFormulaExecEvalMode 设置为 true 后,非 ${ xxx } 格式也使用表达式运算器
163
+ OpenFormulaExecEvalMode = execMode;
164
+ } else if (isString(execMode)) {
165
+ // 指定 execMode 可以直接选用对应的运算器
166
+ curExecMode = execMode;
167
+ }
168
+ if (!isString(value)) {
169
+ // 非字符串类型,直接返回,比如:boolean、number类型、Object、Array类型
170
+ return value;
171
+ } else if (curExecMode && FormulaExec[curExecMode]) {
172
+ return FormulaExec[curExecMode];
173
+ }
174
+
175
+ const curValue = value.trim(); // 剔除前后空格
176
+
177
+ // OpenFormulaExecEvalMode 为 true 时,非 ${ xxx } 格式也会尝试使用表达式运算器
178
+ if (OpenFormulaExecEvalMode && /^[0-9a-zA-z_]+$/.test(curValue)) {
179
+ // 普通字符串类型(非表达式),先试一下从上下文中获取数据
180
+ const curValueTemp = FormulaExec['var'](curValue, data);
181
+ // 备注: 其他特殊格式,比如邮箱、日期
182
+ return curValueTemp ?? curValue;
183
+ } else if (curValue.startsWith('${') && curValue.endsWith('}')) {
184
+ // ${ xxx } 格式 使用 formula 运算器
185
+ return FormulaExec['formula'](curValue, data);
186
+ } else if (isExpression(test)) {
187
+ // 表达式格式使用 formula 运算器
188
+ return FormulaExec['formula'](curValue, data);
189
+ } else if (/(\${).+(\})/.test(curValue)) {
190
+ // 包含 ${ xxx } 则使用 tpl 运算器
191
+ return FormulaExec['tpl'](curValue, data); // 可识别 <% %> 语法
192
+ } else if (OpenFormulaExecEvalMode) {
193
+ // 支持 ${} 和 非 ${} 表达式
194
+ return FormulaExec['evalFormula'](curValue, data);
195
+ } else {
196
+ return curValue;
197
+ }
198
+ }
199
+
200
+ // 用于注册自定义 formulaExec 运算器
201
+ export function registerFormulaExec(execMode: string, formulaExec: Function) {
202
+ if (FormulaExec[execMode]) {
203
+ console.error(
204
+ `registerFormulaExec: 运算器注册失败,存在同名运算器($(execMode))。`
205
+ );
206
+ } else {
207
+ FormulaExec[execMode] = formulaExec;
208
+ }
209
+ }
210
+
211
+ // 用于判断是否优先使用value。
212
+ export function isExpression(expression: any): boolean {
213
+ if (!isString(expression)) {
214
+ // 非字符串类型,比如:Object、Array类型、boolean、number类型
215
+ return false;
216
+ }
217
+ // 备注: "\\${xxx}"不作为表达式,至少含一个${xxx}才算是表达式
218
+ return /(?<!\\)(\${).+(\})/.test(expression);
219
+ }
220
+
221
+ // 用于判断是否需要执行表达式:
222
+ export function isNeedFormula(
223
+ expression: any,
224
+ prevData: {[propName: string]: any},
225
+ curData: {[propName: string]: any}
226
+ ): boolean {
227
+ const variables = FormulaExec.collect(expression);
228
+ return variables.some(
229
+ (variable: string) =>
230
+ FormulaExec.var(variable, prevData) !== FormulaExec.var(variable, curData)
231
+ );
232
+ }
233
+
234
+ // 将 \${xx} 替换成 ${xx}
235
+ export function replaceExpression(expression: any): any {
236
+ if (expression && isString(expression) && /(\\)(\${).+(\})/.test(expression)) {
237
+ return expression.replace(/\\\$\{/g, '${');
238
+ }
239
+ return expression;
240
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @file 公式语法解析
3
+ */
4
+
5
+ import {parse} from 'amis-formula';
6
+
7
+ function traverseAst(ast: any, iterator: (ast: any) => void) {
8
+ if (!ast || !ast.type) {
9
+ return;
10
+ }
11
+
12
+ iterator(ast);
13
+
14
+ Object.keys(ast).forEach(key => {
15
+ const value = ast[key];
16
+
17
+ if (Array.isArray(value)) {
18
+ value.forEach(child => traverseAst(child, iterator));
19
+ } else {
20
+ traverseAst(value, iterator);
21
+ }
22
+ });
23
+ }
24
+
25
+ // 缓存,用于提升性能
26
+ const COLLECT_EXPRESSION_CACHE: {[key: string]: Array<string>} = {};
27
+
28
+ // 提取表达式中有哪些变量
29
+ export function collectVariables(strOrAst: string | Object, execMode?: boolean): Array<string> {
30
+ const variables: Array<string> = [];
31
+
32
+ if (typeof strOrAst === 'string' && COLLECT_EXPRESSION_CACHE[strOrAst]) {
33
+ return COLLECT_EXPRESSION_CACHE[strOrAst];
34
+ }
35
+ const ast =
36
+ typeof strOrAst === 'string'
37
+ ? parse(strOrAst, {
38
+ evalMode: execMode ?? false
39
+ })
40
+ : strOrAst;
41
+
42
+ traverseAst(ast, (item: any) => {
43
+ if (item.type === 'variable') {
44
+ variables.push(item.name);
45
+ }
46
+ });
47
+
48
+ if (typeof strOrAst === 'string') {
49
+ COLLECT_EXPRESSION_CACHE[strOrAst] = variables;
50
+ }
51
+
52
+ return variables;
53
+ }
@@ -20,7 +20,7 @@ import {
20
20
  keyToPath,
21
21
  isPureVariable,
22
22
  resolveVariable,
23
- resolveVariableAndFilter
23
+ resolveVariableAndFilter,
24
24
  } from 'amis-formula';
25
25
  import {isObservable} from 'mobx';
26
26
 
@@ -1424,6 +1424,7 @@ export function getScrollbarWidth() {
1424
1424
  return scrollbarWidth;
1425
1425
  }
1426
1426
 
1427
+ // 后续改用 FormulaExec['formula']
1427
1428
  function resolveValueByName(data: any, name?: string) {
1428
1429
  return isPureVariable(name)
1429
1430
  ? resolveVariableAndFilter(name, data)
package/src/utils/tpl.ts CHANGED
@@ -71,6 +71,7 @@ export function evalExpression(expression: string, data?: object): boolean {
71
71
  return evalFormula(expression, data);
72
72
  }
73
73
 
74
+ // 后续改用 FormulaExec['js']
74
75
  let debug = false;
75
76
  const idx = expression.indexOf('debugger');
76
77
  if (~idx) {
@@ -149,6 +150,7 @@ export function evalJS(js: string, data: object): any {
149
150
  }
150
151
 
151
152
  [registerBulitin, registerLodash].forEach(fn => {
153
+ if (!fn) return;
152
154
  const info = fn();
153
155
 
154
156
  registerTplEnginer(info.name, {