form-driver 0.4.1 → 0.4.3

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.
@@ -1,28 +1,38 @@
1
1
  import React from "react";
2
2
  import _ from "lodash";
3
3
  import "./AForm.less";
4
- import { Viewer, ViewerState } from '../../BaseViewer';
5
- import { M3UISpec, MFieldSchema, MProp } from '../../../framework/Schema';
4
+ import { Viewer, ViewerState } from "../../BaseViewer";
5
+ import { M3UISpec, MFieldSchema, MProp } from "../../../framework/Schema";
6
6
  import { HideMap, MUtil } from "../../../framework/MUtil";
7
- import { MFieldViewer } from '../../../framework/MFieldViewer';
8
- import { Segment } from '../../widget/Segment';
9
- import { MORPH } from '../../../framework/Assembly';
7
+ import { MFieldViewer } from "../../../framework/MFieldViewer";
8
+ import { Segment } from "../../widget/Segment";
9
+ import { MORPH } from "../../../framework/Assembly";
10
10
  import { SegmentEditSwitch } from "../../widget/SegmentEditSwitch";
11
11
  import { Modal, Popover } from "antd";
12
12
  import { QuestionCircleTwoTone } from "@ant-design/icons";
13
13
  import { MContext } from "../../../framework/MContext";
14
14
  import { Collapsible } from "../../widget/Collapsible";
15
15
 
16
- function ItemLabel(props: { uispec?: M3UISpec, schema: MFieldSchema, labelWidth?: number, morph: MORPH }): JSX.Element {
16
+ function ItemLabel(props: {
17
+ uispec?: M3UISpec;
18
+ schema: MFieldSchema;
19
+ labelWidth?: number;
20
+ morph: MORPH;
21
+ }): JSX.Element {
17
22
  // label自动加冒号功能 uispec.comma
18
23
  let label = props.schema.label;
19
24
  if (props.uispec?.comma) {
20
- // @ts-ignore trimEnd支持正则的,但lodash的声明写得不对
21
- label = _.trimEnd(label, new RegExp("::" + props.uispec.comma)) + props.uispec.comma;
25
+ label =
26
+ // @ts-ignore trimEnd支持正则的,但lodash的声明写得不对
27
+ _.trimEnd(label, new RegExp("::" + props.uispec.comma)) +
28
+ props.uispec.comma;
22
29
  }
23
30
 
24
31
  // 必填label加星号
25
- const star = (props.schema.required && props.morph === "editor") ? <span style={{ color: "red" }}>*</span> : undefined;
32
+ const star =
33
+ props.schema.required && props.morph === "editor" ? (
34
+ <span style={{ color: "red" }}>*</span>
35
+ ) : undefined;
26
36
 
27
37
  if (!props.schema.label) {
28
38
  return <></>;
@@ -30,17 +40,33 @@ function ItemLabel(props: { uispec?: M3UISpec, schema: MFieldSchema, labelWidth?
30
40
 
31
41
  let popoverDesc = undefined;
32
42
  if (props.schema.popoverDesc) {
33
- popoverDesc = <Popover key=":popoverDesc" content={props.schema.popoverDesc}>
34
- <QuestionCircleTwoTone style={{ marginLeft: 5 }} />
35
- </Popover>
43
+ popoverDesc = (
44
+ <Popover key=":popoverDesc" content={props.schema.popoverDesc}>
45
+ <QuestionCircleTwoTone style={{ marginLeft: 5 }} />
46
+ </Popover>
47
+ );
36
48
  }
37
49
 
38
- console.log('ItemLabel', props)
39
50
  if (props.labelWidth) {
40
51
  // 防止 提示信息 换行
41
- return <span className="ItemLabel" style={{ display: "inline-block", width: props.labelWidth + 40 }}>{star}{label}{popoverDesc}</span>;
52
+ return (
53
+ <span
54
+ className="ItemLabel"
55
+ style={{ display: "inline-block", width: props.labelWidth + 40 }}
56
+ >
57
+ {star}
58
+ {label}
59
+ {popoverDesc}
60
+ </span>
61
+ );
42
62
  } else {
43
- return <div className="ItemLabel" key={"字段标题:" + props.schema.name}>{star}{label}{popoverDesc}</div>;
63
+ return (
64
+ <div className="ItemLabel" key={"字段标题:" + props.schema.name}>
65
+ {star}
66
+ {label}
67
+ {popoverDesc}
68
+ </div>
69
+ );
44
70
  }
45
71
  }
46
72
 
@@ -57,7 +83,7 @@ interface State extends ViewerState {
57
83
 
58
84
  // /**
59
85
  // * 判断是否应该使用动画。
60
- // *
86
+ // *
61
87
  // * 当相邻的题,状态变化不同向(都是隐藏,或者都是出现)时,不能使用折叠动画,否则会出现抖动
62
88
  // * 这个函数判断所有的字段,是否需要折叠动画
63
89
  // */
@@ -74,7 +100,7 @@ interface State extends ViewerState {
74
100
  // return 0;
75
101
  // }
76
102
  // })
77
-
103
+
78
104
  // // 监视哨
79
105
  // direction[-1] = 0;
80
106
  // direction[fields.length] = 0;
@@ -94,92 +120,146 @@ interface State extends ViewerState {
94
120
  export class AForm extends Viewer<State> {
95
121
  constructor(p: MProp) {
96
122
  super(p);
97
- this.state = {
98
- //shouldAnimation:{},
99
- editing: {}, saving: {}, ctrlVersion: 1, noValidate: true };
123
+ this.state = {
124
+ //shouldAnimation:{},
125
+ editing: {},
126
+ saving: {},
127
+ ctrlVersion: 1,
128
+ noValidate: true,
129
+ };
100
130
  }
101
131
 
102
132
  /**
103
133
  * 表单项,包括label和viewer
104
- * @param hideField
105
- * @param hideMap
106
- * @param f
107
- * @param objectFields
108
- * @param uispec
109
- * @param morph
110
- * @param labelWidth
111
- * @param invalidLayoutMsg
134
+ * @param hideField
135
+ * @param hideMap
136
+ * @param f
137
+ * @param objectFields
138
+ * @param uispec
139
+ * @param morph
140
+ * @param labelWidth
141
+ * @param invalidLayoutMsg
112
142
  * @param column
113
- * @returns
143
+ * @returns
114
144
  */
115
- private formItem(hideField: boolean, hideMap:HideMap, f: MFieldSchema, objectFields: MFieldSchema[], uispec, morph: MORPH,
116
- labelWidth: number, invalidLayoutMsg: string, column: number): JSX.Element {
117
-
118
- const path = MUtil.jsonPath(this.props.path, f.name)
145
+ private formItem(
146
+ hideField: boolean,
147
+ hideMap: HideMap,
148
+ f: MFieldSchema,
149
+ objectFields: MFieldSchema[],
150
+ uispec,
151
+ morph: MORPH,
152
+ labelWidth: number,
153
+ invalidLayoutMsg: string,
154
+ column: number
155
+ ): JSX.Element {
156
+ const path = MUtil.jsonPath(this.props.path, f.name);
119
157
  const wrapperProp = {
120
158
  style: {
121
159
  display: undefined,
122
160
  marginBottom: 15,
123
- content: f.name // debug用
161
+ content: f.name, // debug用
124
162
  },
125
163
  open: !hideField,
126
164
  //ms: this.state.shouldAnimation?.[f.name] ? 500 : null,
127
165
  ms: 500,
128
166
  key: f.name,
129
167
  id: path,
130
- name: f.name
168
+ name: f.name,
131
169
  };
132
170
  if (column > 1) {
133
171
  // 计算每项的宽度(保留2位有效数字)
134
172
  Object.assign(wrapperProp.style, {
135
- width: Math.floor(100 / column * 100) / 100 + '%'
136
- })
173
+ width: Math.floor((100 / column) * 100) / 100 + "%",
174
+ });
137
175
  }
138
176
 
139
- const fieldViewer = <MFieldViewer morph={morph ?? "readable"} key={path} schema={f} database={this.props.database} path={path} afterChange={(p, v: any, blur) => {
140
- this.props.afterChange?.(path, v, blur);
141
- const newHideMap = MUtil.hideMap(MUtil.get(this.props.database, this.props.path), objectFields, uispec);
142
- if (!_.isEqual(newHideMap, hideMap)) { // 如果有字段依赖导致表单项展示与否变化
143
- //this.setState({shouldAnimation: shouldAnimation(hideMap, newHideMap, objectFields)});
144
- this.setState({});
145
- }
146
- }} parent={this.props.schema} changeSchema={this.props.changeSchema} changeDatabase={this.props.changeDatabase} forceValid={this.props.forceValid} style={{ width: "100%" }} />
177
+ const fieldViewer = (
178
+ <MFieldViewer
179
+ morph={morph ?? "readable"}
180
+ key={path}
181
+ schema={f}
182
+ database={this.props.database}
183
+ path={path}
184
+ afterChange={(p, v: any, blur) => {
185
+ this.props.afterChange?.(path, v, blur);
186
+ const newHideMap = MUtil.hideMap(
187
+ MUtil.get(this.props.database, this.props.path),
188
+ objectFields,
189
+ uispec
190
+ );
191
+ if (!_.isEqual(newHideMap, hideMap)) {
192
+ // 如果有字段依赖导致表单项展示与否变化
193
+ //this.setState({shouldAnimation: shouldAnimation(hideMap, newHideMap, objectFields)});
194
+ this.setState({});
195
+ }
196
+ }}
197
+ parent={this.props.schema}
198
+ changeSchema={this.props.changeSchema}
199
+ changeDatabase={this.props.changeDatabase}
200
+ forceValid={this.props.forceValid}
201
+ style={{ width: "100%" }}
202
+ />
203
+ );
147
204
 
148
205
  let ele;
149
- if(uispec.layout === "vertical") { // label在字段上面的分段布局
150
- ele = <Collapsible {...wrapperProp}>
151
- <ItemLabel uispec={uispec} schema={f} morph={morph}/>
152
- {fieldViewer}
153
- </Collapsible>
154
- } else if(uispec.layout === "horizontal") { // label在字段左边的分段布局 TODO
206
+ if (uispec.layout === "vertical") {
207
+ // label在字段上面的分段布局
208
+ ele = (
209
+ <Collapsible {...wrapperProp}>
210
+ <ItemLabel uispec={uispec} schema={f} morph={morph} />
211
+ {fieldViewer}
212
+ </Collapsible>
213
+ );
214
+ } else if (uispec.layout === "horizontal") {
215
+ // label在字段左边的分段布局 TODO
155
216
  wrapperProp.style.display = "flex";
156
- ele = <Collapsible {...wrapperProp}>
157
- <ItemLabel uispec={uispec} schema={f} labelWidth={labelWidth} morph={morph}/>
158
- <span style={{width: `calc(100% - ${labelWidth}px)`}}>{fieldViewer}</span>
159
- </Collapsible>
217
+ ele = (
218
+ <Collapsible {...wrapperProp}>
219
+ <ItemLabel
220
+ uispec={uispec}
221
+ schema={f}
222
+ labelWidth={labelWidth}
223
+ morph={morph}
224
+ />
225
+ <span style={{ width: `calc(100% - ${labelWidth}px)` }}>
226
+ {fieldViewer}
227
+ </span>
228
+ </Collapsible>
229
+ );
160
230
  } else {
161
231
  ele = MUtil.error(invalidLayoutMsg);
162
232
  }
163
233
 
164
- return <MContext.Consumer key={f.name}>
165
- {ctx => {
166
- if (ctx.rootProps.formItemWrapper) {
167
- return ctx.rootProps.formItemWrapper(ele, f)
168
- } else {
169
- return ele
170
- }
171
- }}
172
- </MContext.Consumer>
234
+ return (
235
+ <MContext.Consumer key={f.name}>
236
+ {(ctx) => {
237
+ if (ctx.rootProps.formItemWrapper) {
238
+ return ctx.rootProps.formItemWrapper(ele, f);
239
+ } else {
240
+ return ele;
241
+ }
242
+ }}
243
+ </MContext.Consumer>
244
+ );
173
245
  }
174
246
 
175
247
  /**
176
248
  * 分段表单
177
- * @param objectFields
178
- * @param uispec
249
+ * @param objectFields
250
+ * @param uispec
179
251
  */
180
- private _segmentForm(objectFields: MFieldSchema[], uispec: M3UISpec, column: number) {
181
- const objectFieldMap = _.chain(objectFields).keyBy('name').value();
182
- const hideMap = MUtil.hideMap(MUtil.get(this.props.database, this.props.path), objectFields, uispec);
252
+ private _segmentForm(
253
+ objectFields: MFieldSchema[],
254
+ uispec: M3UISpec,
255
+ column: number
256
+ ) {
257
+ const objectFieldMap = _.chain(objectFields).keyBy("name").value();
258
+ const hideMap = MUtil.hideMap(
259
+ MUtil.get(this.props.database, this.props.path),
260
+ objectFields,
261
+ uispec
262
+ );
183
263
  if (!uispec.segments) {
184
264
  return MUtil.error("分段未定义");
185
265
  }
@@ -194,7 +274,7 @@ export class AForm extends Viewer<State> {
194
274
  return MUtil.error(`segments中的${fieldName}未定义`);
195
275
  }
196
276
  let label = f.label;
197
- labelWidth = Math.max(labelWidth, MUtil.antdTextWidth(label ?? ""))
277
+ labelWidth = Math.max(labelWidth, MUtil.antdTextWidth(label ?? ""));
198
278
  }
199
279
  }
200
280
  }
@@ -219,9 +299,21 @@ export class AForm extends Viewer<State> {
219
299
  // segment里的字段名都记下来
220
300
  segmentFieldNames.push(fieldName);
221
301
 
222
- items.push(this.formItem(hideField, hideMap, f, objectFields, uispec,
223
- ((this.props.morph === "editor" || this.state.editing[segment.label]) ? "editor" : "readable"),
224
- labelWidth, `${segment.label}的layout值无效:${uispec.layout}`, column))
302
+ items.push(
303
+ this.formItem(
304
+ hideField,
305
+ hideMap,
306
+ f,
307
+ objectFields,
308
+ uispec,
309
+ this.props.morph === "editor" || this.state.editing[segment.label]
310
+ ? "editor"
311
+ : "readable",
312
+ labelWidth,
313
+ `${segment.label}的layout值无效:${uispec.layout}`,
314
+ column
315
+ )
316
+ );
225
317
  }
226
318
 
227
319
  let topRight: React.ReactNode = undefined;
@@ -234,56 +326,94 @@ export class AForm extends Viewer<State> {
234
326
  _.assign(this.props.database, prev);
235
327
  delete this.state.editing[segment.label];
236
328
  this.setState({});
237
- }
238
- topRight = <SegmentEditSwitch state={saving ? "saving" : "editor"} onClick={(e) => {
239
- if (e) {
240
- this.state.saving[segment.label] = true; // 先展示loading动画
241
- onSubmit(segment, _.pick(this.props.database, segmentFieldNames), () => {
242
- // 保存完了以后移除loading状态,并且改回readable模式
329
+ };
330
+ topRight = (
331
+ <SegmentEditSwitch
332
+ state={saving ? "saving" : "editor"}
333
+ onClick={(e) => {
334
+ if (e) {
335
+ this.state.saving[segment.label] = true; // 先展示loading动画
336
+ onSubmit(
337
+ segment,
338
+ _.pick(this.props.database, segmentFieldNames),
339
+ () => {
340
+ // 保存完了以后移除loading状态,并且改回readable模式
341
+ this.setState({
342
+ saving: _.omit(this.state.saving, segment.label),
343
+ editing: _.omit(this.state.saving, segment.label),
344
+ });
345
+ }
346
+ );
347
+ } else {
348
+ const prev = this.state.editing[segment.label];
349
+ const current = _.pick(
350
+ this.props.database,
351
+ segmentFieldNames
352
+ );
353
+ if (!_.isEqual(prev, current)) {
354
+ Modal.confirm({
355
+ content: "会丢失刚才的修改,确定吗?",
356
+ okText: "刚才的修改不要了",
357
+ cancelText: "继续编辑",
358
+ onOk: cancel,
359
+ });
360
+ } else {
361
+ cancel();
362
+ }
363
+ }
364
+ }}
365
+ />
366
+ );
367
+ } else {
368
+ topRight = (
369
+ <SegmentEditSwitch
370
+ state={"readable"}
371
+ onClick={(e) => {
243
372
  this.setState({
244
- saving: _.omit(this.state.saving, segment.label),
245
- editing: _.omit(this.state.saving, segment.label),
246
- });
247
- })
248
- } else {
249
- const prev = this.state.editing[segment.label];
250
- const current = _.pick(this.props.database, segmentFieldNames);
251
- if (!_.isEqual(prev, current)) {
252
- Modal.confirm({
253
- content: '会丢失刚才的修改,确定吗?',
254
- okText: "刚才的修改不要了",
255
- cancelText: "继续编辑",
256
- onOk: cancel
373
+ editing: _.set(
374
+ this.state.editing,
375
+ segment.label,
376
+ _.pick(this.props.database, segmentFieldNames)
377
+ ),
257
378
  });
258
- } else {
259
- cancel();
260
- }
261
- }
262
- }} />
263
- } else {
264
- topRight = <SegmentEditSwitch state={"readable"} onClick={(e) => {
265
- this.setState({
266
- editing: _.set(this.state.editing, segment.label, _.pick(this.props.database, segmentFieldNames))
267
- });
268
- }} />
379
+ }}
380
+ />
381
+ );
269
382
  }
270
383
  }
271
384
 
272
- segments.push(<Segment column={column} style={{ ...segment.style, display: segHide ? "none" : undefined }} key={fno++} mainTitle={segment.label} labelName={"segment_" + fno} topRight={topRight}>
385
+ segments.push(
386
+ <Segment
387
+ column={column}
388
+ style={{ ...segment.style, display: segHide ? "none" : undefined }}
389
+ key={fno++}
390
+ mainTitle={segment.label}
391
+ labelName={"segment_" + fno}
392
+ topRight={topRight}
393
+ >
273
394
  {items}
274
- </Segment>);
395
+ </Segment>
396
+ );
275
397
  }
276
- return <div className="AForm">{segments}</div>
398
+ return <div className="AForm">{segments}</div>;
277
399
  }
278
400
 
279
401
  /**
280
402
  * 不分段的表单
281
- * @param objectFields
282
- * @param uispec
283
- * @param column
403
+ * @param objectFields
404
+ * @param uispec
405
+ * @param column
284
406
  */
285
- private _flowForm(objectFields: MFieldSchema[], uispec: M3UISpec, column: number) {
286
- const hideMap = MUtil.hideMap(MUtil.get(this.props.database, this.props.path), objectFields, uispec);
407
+ private _flowForm(
408
+ objectFields: MFieldSchema[],
409
+ uispec: M3UISpec,
410
+ column: number
411
+ ) {
412
+ const hideMap = MUtil.hideMap(
413
+ MUtil.get(this.props.database, this.props.path),
414
+ objectFields,
415
+ uispec
416
+ );
287
417
 
288
418
  // 先计算一遍label的宽度,即使有隐藏的字段,确保展示出来时也不会因为label太长布局变化。
289
419
  // table防止折行是保底的
@@ -291,27 +421,42 @@ export class AForm extends Viewer<State> {
291
421
  if (uispec.layout === "horizontal") {
292
422
  for (let f of objectFields) {
293
423
  let label = f.label;
294
- labelWidth = Math.max(labelWidth, MUtil.antdTextWidth(label ?? ""))
424
+ labelWidth = Math.max(labelWidth, MUtil.antdTextWidth(label ?? ""));
295
425
  }
296
426
  }
297
427
 
298
428
  // 然后再把表单表格画出来
299
429
  const items = [];
300
430
  for (let f of objectFields) {
301
- items.push(this.formItem(hideMap[f.name], hideMap, f, objectFields, uispec,
302
- this.props.morph, labelWidth, `layout值无效:${uispec.layout}`, column
303
- ))
431
+ items.push(
432
+ this.formItem(
433
+ hideMap[f.name],
434
+ hideMap,
435
+ f,
436
+ objectFields,
437
+ uispec,
438
+ this.props.morph,
439
+ labelWidth,
440
+ `layout值无效:${uispec.layout}`,
441
+ column
442
+ )
443
+ );
304
444
  }
305
445
  if (column > 1) {
306
- return <div className="AForm" style={{
307
- display: 'flex',
308
- flexFlow: 'row wrap',
309
- alignContent: 'flex-start',
310
- }}>
311
- {items}
312
- </div>
446
+ return (
447
+ <div
448
+ className="AForm"
449
+ style={{
450
+ display: "flex",
451
+ flexFlow: "row wrap",
452
+ alignContent: "flex-start",
453
+ }}
454
+ >
455
+ {items}
456
+ </div>
457
+ );
313
458
  } else {
314
- return <div className="AForm">{items}</div>
459
+ return <div className="AForm">{items}</div>;
315
460
  }
316
461
  }
317
462
 
@@ -321,7 +466,10 @@ export class AForm extends Viewer<State> {
321
466
  return MUtil.error(`表单是空的`, this.props.schema);
322
467
  }
323
468
 
324
- const uispec = this.props.schema.uispec ?? { type: "flowForm", layout: "vertical" };
469
+ const uispec = this.props.schema.uispec ?? {
470
+ type: "flowForm",
471
+ layout: "vertical",
472
+ };
325
473
  const column = this.props.schema.column ?? 1;
326
474
  switch (this.props.schema.uispec?.type) {
327
475
  case "segmentForm": // 分段的表单
@@ -337,4 +485,4 @@ export class AForm extends Viewer<State> {
337
485
  return this._flowForm(objectFields, uispec, column);
338
486
  }
339
487
  }
340
- }
488
+ }