form-driver 0.4.19 → 0.4.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-driver",
3
- "version": "0.4.19",
3
+ "version": "0.4.21",
4
4
  "description": "An efficient framework for creating forms.",
5
5
  "license": "MIT",
6
6
  "authors": [
@@ -61,7 +61,7 @@
61
61
  "@babel/runtime": "^7.9.2",
62
62
  "ali-oss": "^6.17.1",
63
63
  "antd": "4.18.7",
64
- "antd-mobile": "^2.3.4",
64
+ "antd-mobile": "^5.37.1",
65
65
  "clsx": "^2.1.1",
66
66
  "lodash": "^4.17.20",
67
67
  "moment": "^2.29.1",
@@ -45,6 +45,8 @@ import { ATable } from "../ui/editor/complex/ATable";
45
45
  import { ARemoteSelector } from "../ui/editor/basic/ARemoteSelector";
46
46
  import { JsonEditor } from "../ui/editor/complex/JsonEditor";
47
47
  import _ from "lodash";
48
+ import moment from "moment";
49
+ import "moment/locale/zh-cn";
48
50
  import { A } from "../ui/readable/A";
49
51
  import { ADialogForm } from "../ui/editor/complex/ADialogForm";
50
52
  import { MVLPairType } from "../types/MVLPairType";
@@ -68,9 +70,11 @@ export function ensureM3() {
68
70
  return;
69
71
  }
70
72
  init = true;
73
+ moment.locale('zh-cn');
71
74
  assembly.types = _.merge(assembly.types, {
72
75
  enum: MEnumType,
73
76
  gb2260: MGB2260Type,
77
+ date: MDateTimeType,
74
78
  datetime: MDateTimeType,
75
79
  year: MDateTimeType,
76
80
  yearMonth: MDateTimeType,
@@ -138,6 +142,7 @@ export function ensureM3() {
138
142
  editor: {
139
143
  enum: "ASelector",
140
144
  gb2260: "AGB2260",
145
+ date: "ADatetimePicker",
141
146
  datetime: "ADatetimePicker",
142
147
  year: "ADatetimePicker",
143
148
  yearMonth: "ADatetimePicker",
@@ -171,6 +176,7 @@ export function ensureM3() {
171
176
  readable: {
172
177
  enum: "DivViewer",
173
178
  gb2260: "DivViewer",
179
+ date: "DivViewer",
174
180
  datetime: "DivViewer",
175
181
  year: "DivViewer",
176
182
  yearMonth: "DivViewer",
@@ -10,6 +10,8 @@ import {
10
10
  import { JSONSchema6 } from "json-schema";
11
11
  import React from "react";
12
12
  import { SchemaFunc } from "./SchemaFunc";
13
+ import { MDateTimeType } from "../types/MDateTimeType";
14
+ import { getReadableFormat } from "../types/MDateRangeType";
13
15
 
14
16
  export type HideMap = { [fieldName: string]: boolean };
15
17
 
@@ -553,6 +555,149 @@ export let MUtil = {
553
555
  return result;
554
556
  },
555
557
 
558
+ /**
559
+ * 提交时将时间字段转换为可读格式
560
+ * @param schema 表单 schema(type 为 object 的根 schema)
561
+ * @param database 表单数据
562
+ * @returns 深拷贝后转换过的数据
563
+ */
564
+ formatForExport: function (schema: MFieldSchemaAnonymity, database: any): any {
565
+ if (_.isNil(database)) {
566
+ return database;
567
+ }
568
+ const result = _.cloneDeep(database);
569
+ const fields = schema.objectFields;
570
+ if (!fields) {
571
+ return result;
572
+ }
573
+ for (const field of fields) {
574
+ const value = result[field.name];
575
+ if (_.isNil(value)) {
576
+ continue;
577
+ }
578
+ const type = field.type;
579
+ // 时间选择器
580
+ if (type === "year" || type === "yearMonth" || type === "yearMonthDay" || type === "datetime" || type === "date") {
581
+ const antConf = MDateTimeType.antConf(field);
582
+ if (antConf) {
583
+ const m = moment(value, antConf.dataFormat);
584
+ if (m.isValid()) {
585
+ result[field.name] = m.format(antConf.readableFormat);
586
+ }
587
+ }
588
+ }
589
+ // 时间范围选择器
590
+ else if (type === "dateRange") {
591
+ if (_.isArray(value)) {
592
+ const [start, end, tillNow] = value;
593
+ const fmt = getReadableFormat(field.dateRangePrecision, field.dateRange?.showTime);
594
+ const dataFormat = field.dataFormat ?? "x";
595
+ let startStr = "";
596
+ let endStr = "";
597
+ if (!_.isNil(start)) {
598
+ const m = moment(start, dataFormat);
599
+ startStr = m.isValid() ? m.format(fmt) : "";
600
+ }
601
+ if (tillNow) {
602
+ endStr = "至今";
603
+ } else if (!_.isNil(end)) {
604
+ const m = moment(end, dataFormat);
605
+ endStr = m.isValid() ? m.format(fmt) : "";
606
+ }
607
+ result[field.name] = startStr + " - " + endStr;
608
+ }
609
+ }
610
+ // 嵌套 object
611
+ else if (type === "object" && field.objectFields) {
612
+ result[field.name] = MUtil.formatForExport(field, value);
613
+ }
614
+ // 嵌套 array
615
+ else if (type === "array" && field.arrayMember && _.isArray(value)) {
616
+ if (field.arrayMember.objectFields) {
617
+ result[field.name] = value.map((item: any) =>
618
+ MUtil.formatForExport(field.arrayMember!, item)
619
+ );
620
+ }
621
+ }
622
+ }
623
+ return result;
624
+ },
625
+
626
+ /**
627
+ * 回填时将可读格式的时间字段反解析为内部格式
628
+ * @param schema 表单 schema(type 为 object 的根 schema)
629
+ * @param database 可读格式的数据
630
+ * @returns 深拷贝后反解析过的数据
631
+ */
632
+ parseFromExport: function (schema: MFieldSchemaAnonymity, database: any): any {
633
+ if (_.isNil(database)) {
634
+ return database;
635
+ }
636
+ const result = _.cloneDeep(database);
637
+ const fields = schema.objectFields;
638
+ if (!fields) {
639
+ return result;
640
+ }
641
+ for (const field of fields) {
642
+ const value = result[field.name];
643
+ if (_.isNil(value)) {
644
+ continue;
645
+ }
646
+ const type = field.type;
647
+ // 时间选择器
648
+ if (type === "year" || type === "yearMonth" || type === "yearMonthDay" || type === "datetime" || type === "date") {
649
+ const antConf = MDateTimeType.antConf(field);
650
+ if (antConf && _.isString(value)) {
651
+ const m = moment(value, antConf.readableFormat);
652
+ if (m.isValid()) {
653
+ result[field.name] = m.format(antConf.dataFormat);
654
+ }
655
+ }
656
+ }
657
+ // 时间范围选择器
658
+ else if (type === "dateRange") {
659
+ if (_.isString(value)) {
660
+ const fmt = getReadableFormat(field.dateRangePrecision, field.dateRange?.showTime);
661
+ const dataFormat = field.dataFormat ?? "x";
662
+ const parts = value.split(" - ");
663
+ const startStr = parts[0]?.trim();
664
+ const endStr = parts[1]?.trim();
665
+ let start: string | null = null;
666
+ let end: string | null = null;
667
+ let tillNow = false;
668
+ if (startStr) {
669
+ const m = moment(startStr, fmt);
670
+ if (m.isValid()) {
671
+ start = m.format(dataFormat);
672
+ }
673
+ }
674
+ if (endStr === "至今") {
675
+ tillNow = true;
676
+ } else if (endStr) {
677
+ const m = moment(endStr, fmt);
678
+ if (m.isValid()) {
679
+ end = m.format(dataFormat);
680
+ }
681
+ }
682
+ result[field.name] = [start, end, tillNow];
683
+ }
684
+ }
685
+ // 嵌套 object
686
+ else if (type === "object" && field.objectFields) {
687
+ result[field.name] = MUtil.parseFromExport(field, value);
688
+ }
689
+ // 嵌套 array
690
+ else if (type === "array" && field.arrayMember && _.isArray(value)) {
691
+ if (field.arrayMember.objectFields) {
692
+ result[field.name] = value.map((item: any) =>
693
+ MUtil.parseFromExport(field.arrayMember!, item)
694
+ );
695
+ }
696
+ }
697
+ }
698
+ return result;
699
+ },
700
+
556
701
  /** 啥也不干的空回调 */
557
702
  doNothing: function () {},
558
703
 
@@ -183,14 +183,28 @@ export interface MFieldSchema {
183
183
  overlap?: boolean;
184
184
  };
185
185
 
186
+ /** date 类型配置 */
187
+ date?: {
188
+ /** 日期精度:month=年月, day=年月日(默认), minute=年月日时分 */
189
+ precision?: "month" | "day" | "minute";
190
+ };
191
+
192
+ /** 日期精度(扁平化属性):year=年, month=年月, day=年月日(默认), minute=年月日时分 */
193
+ datePrecision?: string;
194
+
186
195
  /** dateRange 类型配置 */
187
196
  dateRange?: {
188
197
  /** 是否隐藏至今按钮 */
189
198
  hideTillNow?: boolean;
190
199
  /** 是否能选择时间 */
191
200
  showTime?: boolean;
201
+ /** 日期精度:month=年月, day=年月日(默认), minute=年月日时分 */
202
+ precision?: "month" | "day" | "minute";
192
203
  };
193
204
 
205
+ /** 日期范围精度(扁平化属性) */
206
+ dateRangePrecision?: string;
207
+
194
208
  /** 数据格式 */
195
209
  dataFormat?:
196
210
  | "x"
@@ -320,7 +334,7 @@ export interface MFieldSchema {
320
334
  export type AFTER_CHANGE_CALLBACK = (
321
335
  path: string,
322
336
  v: any,
323
- final: boolean
337
+ final: boolean,
324
338
  ) => void;
325
339
 
326
340
  export type CHANGE_SCHEMA_CALLBACK = (v: any) => void;
@@ -384,7 +398,7 @@ export interface M3UISpecSegmentItem {
384
398
  onSubmit?: (
385
399
  segment: M3UISpecSegmentItem,
386
400
  segmentData: any,
387
- done: () => void
401
+ done: () => void,
388
402
  ) => void;
389
403
 
390
404
  /** 设置分段根元素的style */
package/src/index.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import 'antd/dist/antd.css';
2
- import 'antd-mobile/dist/antd-mobile.css';
3
2
  import { Ajax } from './framework/Ajax';
4
3
  import { M3UISpecSegmentItem, MFieldSchema, M3UISpec, MFieldSchemaAnonymity } from './framework/Schema';
5
4
  import { MViewerDebug } from './framework/MViewerDebug';
@@ -1,52 +1,93 @@
1
-
2
-
3
-
4
1
  import { MFieldSchemaAnonymity } from "../framework/Schema";
5
- import { EmtpyType, MType} from "./MType";
2
+ import { EmtpyType, MType } from "./MType";
6
3
  import { validateRequired } from "../framework/Validator";
7
4
  import { Assembly } from "../framework/Assembly";
8
5
  import moment from "moment";
9
6
  import _ from "lodash";
10
7
 
11
- function timeExpr(assembly:Assembly, v:any, tillNow:boolean, readableFormat):string {
12
- if(tillNow){
8
+ /** 根据 precision 获取可读格式 */
9
+ export function getReadableFormat(precision?: string, showTime?: boolean): string {
10
+ switch (precision) {
11
+ case "year":
12
+ return "YYYY";
13
+ case "month":
14
+ return "YYYY.MM";
15
+ case "minute":
16
+ return "YYYY.MM.DD HH:mm";
17
+ default:
18
+ return showTime ? "YYYY.MM.DD HH:mm:ss" : "YYYY.MM.DD";
19
+ }
20
+ }
21
+
22
+ function timeExpr(
23
+ assembly: Assembly,
24
+ v: any,
25
+ tillNow: boolean,
26
+ readableFormat,
27
+ ): string {
28
+ if (tillNow) {
13
29
  return "至今";
14
30
  }
15
- if(!v){
31
+ if (!v) {
16
32
  return assembly.theme.READABLE_UNKNOWN;
17
33
  }
18
34
  return moment(v, "x").format(readableFormat);
19
35
  }
20
36
 
21
- export function timeRangeExpr(assembly:Assembly, from:number|string, to:number|string, tillNow:boolean, readableFormat:string = "YYYY年MM月DD日"):string {
22
- return timeExpr(assembly, from, false, readableFormat) + " ~ " + timeExpr(assembly, to, tillNow, readableFormat)
37
+ export function timeRangeExpr(
38
+ assembly: Assembly,
39
+ from: number | string,
40
+ to: number | string,
41
+ tillNow: boolean,
42
+ readableFormat: string = "YYYY.MM.DD",
43
+ ): string {
44
+ return (
45
+ timeExpr(assembly, from, false, readableFormat) +
46
+ " - " +
47
+ timeExpr(assembly, to, tillNow, readableFormat)
48
+ );
23
49
  }
24
50
 
25
- export const MDateRangeType: MType & {toReadableN: (assembly:Assembly, s:MFieldSchemaAnonymity, vs:any)=>string|undefined} = _.assign({}, EmtpyType, {
51
+ export const MDateRangeType: MType & {
52
+ toReadableN: (
53
+ assembly: Assembly,
54
+ s: MFieldSchemaAnonymity,
55
+ vs: any,
56
+ ) => string | undefined;
57
+ } = _.assign({}, EmtpyType, {
26
58
  validators: [validateRequired],
27
-
28
- toReadable: (assembly:Assembly, s:MFieldSchemaAnonymity, vs:any):string => {
29
- if(_.isNil(vs)){
30
- return assembly.theme.READABLE_BLANK
59
+
60
+ toReadable: (
61
+ assembly: Assembly,
62
+ s: MFieldSchemaAnonymity,
63
+ vs: any,
64
+ ): string => {
65
+ if (_.isNil(vs)) {
66
+ return assembly.theme.READABLE_BLANK;
31
67
  }
32
- if(!_.isArray(vs)){
68
+ if (!_.isArray(vs)) {
33
69
  return assembly.theme.READABLE_INVALID;
34
70
  }
35
71
 
36
- return timeRangeExpr(assembly, vs[0], vs[1], vs[2], s.dateRange?.showTime ? "YYYY年MM月DD日 HH:mm:ss" : "YYYY年MM月DD日");
72
+ const fmt = getReadableFormat(s.dateRangePrecision, s.dateRange?.showTime);
73
+ return timeRangeExpr(assembly, vs[0], vs[1], vs[2], fmt);
37
74
  },
38
75
 
39
76
  /**
40
77
  * 同toReadable,但数据无效时返回nil
41
- * @param assembly
42
- * @param s
43
- * @param vs
44
- * @returns
45
78
  */
46
- toReadableN: (assembly:Assembly, s:MFieldSchemaAnonymity, vs:any):string|undefined => {
47
- if(!_.isNil(vs) && _.isArray(vs)) {
48
- return timeRangeExpr(assembly, vs[0], vs[1], vs[2], s.dateRange?.showTime ? "YYYY年MM月DD日 HH:mm:ss" : "YYYY年MM月DD日");
79
+ toReadableN: (
80
+ assembly: Assembly,
81
+ s: MFieldSchemaAnonymity,
82
+ vs: any,
83
+ ): string | undefined => {
84
+ if (!_.isNil(vs) && _.isArray(vs)) {
85
+ const fmt = getReadableFormat(
86
+ s.dateRangePrecision,
87
+ s.dateRange?.showTime,
88
+ );
89
+ return timeRangeExpr(assembly, vs[0], vs[1], vs[2], fmt);
49
90
  }
50
91
  return undefined;
51
- }
92
+ },
52
93
  });
@@ -1,53 +1,97 @@
1
-
2
- import {validateDateMinMax, validateRequired} from '../framework/Validator';
3
- import { MFieldSchemaAnonymity } from '../framework/Schema';
4
- import { EmtpyType, MType} from "./MType";
5
- import moment from 'moment';
6
- import { Assembly } from '../framework/Assembly';
7
- import _ from 'lodash';
1
+ import { validateDateMinMax, validateRequired } from "../framework/Validator";
2
+ import { MFieldSchemaAnonymity } from "../framework/Schema";
3
+ import { EmtpyType, MType } from "./MType";
4
+ import moment from "moment";
5
+ import { Assembly } from "../framework/Assembly";
6
+ import _ from "lodash";
8
7
 
9
8
  export interface MDateTimeAntConf {
10
- dataFormat: string,
11
- readableFormat: string,
12
- mode: undefined | 'date' | 'year' | 'month' | 'time';
9
+ dataFormat: string;
10
+ readableFormat: string;
11
+ mode: undefined | "date" | "year" | "month" | "time";
13
12
  showTime: boolean;
14
13
  }
15
14
 
16
- export const MDateTimeType:MType & {antConf:(schema:MFieldSchemaAnonymity)=>MDateTimeAntConf|undefined} = _.assign({}, EmtpyType, {
15
+ export const MDateTimeType: MType & {
16
+ antConf: (schema: MFieldSchemaAnonymity) => MDateTimeAntConf | undefined;
17
+ } = _.assign({}, EmtpyType, {
17
18
  validators: [validateRequired, validateDateMinMax],
18
19
 
19
- antConf: (schema:MFieldSchemaAnonymity):MDateTimeAntConf|undefined => {
20
- let dataFormat:string|undefined = schema.dataFormat;
20
+ antConf: (schema: MFieldSchemaAnonymity): MDateTimeAntConf | undefined => {
21
+ let dataFormat: string | undefined = schema.dataFormat;
21
22
  let readableFormat: string;
22
- let mode: undefined | 'date' | 'year' | 'month' | 'time';
23
+ let mode: undefined | "date" | "year" | "month" | "time";
23
24
  let showTime = false;
24
- switch(schema.type) {
25
- case "year":
26
- mode = "year"; dataFormat = dataFormat ?? 'YYYY'; readableFormat = 'YYYY'; break;
27
- case "yearMonth":
28
- mode = "month"; dataFormat = dataFormat ?? 'YYYYMM'; readableFormat = 'YYYY-MM'; break;
29
- case "yearMonthDay":
30
- mode = "date"; dataFormat = dataFormat ?? 'YYYYMMDD'; readableFormat = 'YYYY-MM-DD'; break;
31
- case "datetime":
32
- mode = undefined; dataFormat = dataFormat ?? 'x'; readableFormat = 'YYYY-MM-DD HH:mm'; showTime = true; break;
25
+ switch (schema.type) {
26
+ case "year":
27
+ mode = "year";
28
+ dataFormat = dataFormat ?? "YYYY";
29
+ readableFormat = "YYYY";
30
+ break;
31
+ case "yearMonth":
32
+ mode = "month";
33
+ dataFormat = dataFormat ?? "YYYYMM";
34
+ readableFormat = "YYYY.MM";
35
+ break;
36
+ case "yearMonthDay":
37
+ mode = "date";
38
+ dataFormat = dataFormat ?? "YYYYMMDD";
39
+ readableFormat = "YYYY.MM.DD";
40
+ break;
41
+ case "datetime":
42
+ mode = undefined;
43
+ dataFormat = dataFormat ?? "x";
44
+ readableFormat = "YYYY.MM.DD HH:mm";
45
+ showTime = true;
46
+ break;
47
+ case "date":
48
+ switch (schema.datePrecision) {
49
+ case "year":
50
+ mode = "year";
51
+ dataFormat = dataFormat ?? "YYYY";
52
+ readableFormat = "YYYY";
53
+ break;
54
+ case "month":
55
+ mode = "month";
56
+ dataFormat = dataFormat ?? "YYYYMM";
57
+ readableFormat = "YYYY.MM";
58
+ break;
59
+ case "minute":
60
+ mode = undefined;
61
+ dataFormat = dataFormat ?? "x";
62
+ readableFormat = "YYYY.MM.DD HH:mm";
63
+ showTime = true;
64
+ break;
65
+ default:
66
+ // "day" 或未指定
67
+ mode = "date";
68
+ dataFormat = dataFormat ?? "YYYYMMDD";
69
+ readableFormat = "YYYY.MM.DD";
70
+ break;
71
+ }
72
+ break;
33
73
  // case "yearAndQuarter":
34
74
  // case "yearAndWeek":
35
75
  // return MUtil.error(`移动端不支持${this.props.schema.type}`, this.props.schema);
36
76
  default:
37
77
  return undefined;
38
78
  }
39
- return {dataFormat, readableFormat, mode, showTime};
79
+ return { dataFormat, readableFormat, mode, showTime };
40
80
  },
41
81
 
42
- toReadable: (assembly:Assembly, schema:MFieldSchemaAnonymity, v:any):string => {
43
- if(_.isNil(v)){
82
+ toReadable: (
83
+ assembly: Assembly,
84
+ schema: MFieldSchemaAnonymity,
85
+ v: any,
86
+ ): string => {
87
+ if (_.isNil(v)) {
44
88
  return assembly.theme.READABLE_BLANK;
45
89
  }
46
90
  let c = MDateTimeType.antConf(schema);
47
- if(!c) {
91
+ if (!c) {
48
92
  return assembly.theme.READABLE_INVALID;
49
93
  }
50
- let asMoment = moment(v, c.dataFormat)
94
+ let asMoment = moment(v, c.dataFormat);
51
95
  return asMoment.format(c.readableFormat);
52
- }
96
+ },
53
97
  });
@@ -4,7 +4,7 @@ import _ from "lodash";
4
4
  import { MUtil } from "../../../framework/MUtil";
5
5
  import { Cascader } from "antd";
6
6
  import { MFieldSchema } from "../../../framework/Schema";
7
- import { Picker } from "antd-mobile";
7
+ import { CascadePicker } from "antd-mobile";
8
8
  import { BaseViewer } from "../../..";
9
9
 
10
10
  export function labelExpr(d: any, remote: MFieldSchema["remote"]) {
@@ -75,23 +75,31 @@ export class ACascadePicker extends BaseViewer {
75
75
  if (MUtil.phoneLike()) {
76
76
  return (
77
77
  <>
78
- {/* <Picker
79
- data={this.props.schema.option}
80
- extra="请选择(可选)"
81
- cols={2}
82
- value={show}
83
- onChange={v => {
84
- console.log(v);
85
- super.changeValue(v);
78
+ <div className="backfill" onClick={() => this.setState({ cascadeVisible: true } as any)}>
79
+ {show.length > 0 ? show.map(item => item.label || item.value || item).join('-') : '请点击选择'}
80
+ </div>
81
+ <CascadePicker
82
+ options={candidate}
83
+ value={show.map((item: any) => item.value || item)}
84
+ visible={(this.state as any)?.cascadeVisible}
85
+ onConfirm={(v) => {
86
+ const vLabel: any[] = [];
87
+ (v as any[]).forEach((item) => {
88
+ candidate.forEach((dataItem: any) => {
89
+ const findItem = findById(dataItem, item, "label", "value", "children");
90
+ if (findItem !== -1) {
91
+ vLabel.push(findItem);
92
+ }
93
+ });
94
+ });
95
+ super.changeValue(vLabel);
96
+ this.setState({ cascadeVisible: false } as any);
86
97
  }}
87
- onOk={v => {
88
- console.log(v);
89
- super.changeValue(v);
98
+ onClose={() => {
99
+ this.setState({ cascadeVisible: false } as any);
90
100
  }}
91
101
  {...p}
92
- >
93
- <div className="backfill"> {show.length > 0 ? show.join('-') : '请点击选择' } </div>
94
- </Picker> */}
102
+ />
95
103
  </>
96
104
  );
97
105
  } else {