form-driver 0.4.20 → 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.20",
3
+ "version": "0.4.21",
4
4
  "description": "An efficient framework for creating forms.",
5
5
  "license": "MIT",
6
6
  "authors": [
@@ -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
 
@@ -6,16 +6,16 @@ import moment from "moment";
6
6
  import _ from "lodash";
7
7
 
8
8
  /** 根据 precision 获取可读格式 */
9
- function getReadableFormat(precision?: string, showTime?: boolean): string {
9
+ export function getReadableFormat(precision?: string, showTime?: boolean): string {
10
10
  switch (precision) {
11
11
  case "year":
12
- return "YYYY";
12
+ return "YYYY";
13
13
  case "month":
14
- return "YYYYMM";
14
+ return "YYYY.MM";
15
15
  case "minute":
16
- return "YYYYMMDD HH:mm";
16
+ return "YYYY.MM.DD HH:mm";
17
17
  default:
18
- return showTime ? "YYYYMMDD HH:mm:ss" : "YYYYMMDD";
18
+ return showTime ? "YYYY.MM.DD HH:mm:ss" : "YYYY.MM.DD";
19
19
  }
20
20
  }
21
21
 
@@ -39,11 +39,11 @@ export function timeRangeExpr(
39
39
  from: number | string,
40
40
  to: number | string,
41
41
  tillNow: boolean,
42
- readableFormat: string = "YYYYMMDD",
42
+ readableFormat: string = "YYYY.MM.DD",
43
43
  ): string {
44
44
  return (
45
45
  timeExpr(assembly, from, false, readableFormat) +
46
- " ~ " +
46
+ " - " +
47
47
  timeExpr(assembly, to, tillNow, readableFormat)
48
48
  );
49
49
  }
@@ -31,17 +31,17 @@ export const MDateTimeType: MType & {
31
31
  case "yearMonth":
32
32
  mode = "month";
33
33
  dataFormat = dataFormat ?? "YYYYMM";
34
- readableFormat = "YYYY-MM";
34
+ readableFormat = "YYYY.MM";
35
35
  break;
36
36
  case "yearMonthDay":
37
37
  mode = "date";
38
38
  dataFormat = dataFormat ?? "YYYYMMDD";
39
- readableFormat = "YYYY-MM-DD";
39
+ readableFormat = "YYYY.MM.DD";
40
40
  break;
41
41
  case "datetime":
42
42
  mode = undefined;
43
43
  dataFormat = dataFormat ?? "x";
44
- readableFormat = "YYYY-MM-DD HH:mm";
44
+ readableFormat = "YYYY.MM.DD HH:mm";
45
45
  showTime = true;
46
46
  break;
47
47
  case "date":
@@ -54,19 +54,19 @@ export const MDateTimeType: MType & {
54
54
  case "month":
55
55
  mode = "month";
56
56
  dataFormat = dataFormat ?? "YYYYMM";
57
- readableFormat = "YYYY-MM";
57
+ readableFormat = "YYYY.MM";
58
58
  break;
59
59
  case "minute":
60
60
  mode = undefined;
61
61
  dataFormat = dataFormat ?? "x";
62
- readableFormat = "YYYY-MM-DD HH:mm";
62
+ readableFormat = "YYYY.MM.DD HH:mm";
63
63
  showTime = true;
64
64
  break;
65
65
  default:
66
66
  // "day" 或未指定
67
67
  mode = "date";
68
68
  dataFormat = dataFormat ?? "YYYYMMDD";
69
- readableFormat = "YYYY-MM-DD";
69
+ readableFormat = "YYYY.MM.DD";
70
70
  break;
71
71
  }
72
72
  break;
@@ -118,25 +118,38 @@ export class ARangePicker extends Viewer<State> {
118
118
  key={`end_${mobilePrecision}`}
119
119
  visible={this.state.mobileDlg && this.state.mobileStep === 'end'}
120
120
  precision={mobilePrecision}
121
- title="选择结束日期"
122
- tillNow={!this.props.schema.dateRange?.hideTillNow && !this.props.schema.dateRange?.showTime}
121
+ title={
122
+ // 如果允许"至今"且开始时间不在未来,在标题区域展示"至今"按钮
123
+ !this.props.schema.dateRange?.hideTillNow
124
+ && !this.props.schema.dateRange?.showTime
125
+ && !(this.state.mobileStartDate && this.state.mobileStartDate > new Date())
126
+ ? <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
127
+ <span>选择结束日期</span>
128
+ <AntButton
129
+ size="small"
130
+ type="primary"
131
+ onClick={() => {
132
+ const startDate = this.state.mobileStartDate;
133
+ if (startDate) {
134
+ super.changeValueEx(
135
+ this._rangePicker2Data([moment(startDate), moment()], true),
136
+ true, true
137
+ );
138
+ }
139
+ this.setState({ mobileDlg: false });
140
+ }}
141
+ >至今</AntButton>
142
+ </div>
143
+ : "选择结束日期"
144
+ }
123
145
  min={this.state.mobileStartDate || (this.props.schema.min ? new Date(this.props.schema.min) : undefined)}
124
146
  max={this.props.schema.max ? new Date(this.props.schema.max) : undefined}
125
147
  onConfirm={(val: any) => {
126
148
  const startDate = this.state.mobileStartDate;
127
149
  if (startDate) {
128
- const isTillNow = !!(val as any).tillNow;
129
- if (isTillNow) {
130
- // 用户选择了"至今"
131
- super.changeValueEx(
132
- this._rangePicker2Data([moment(startDate), moment()], true),
133
- true, true
134
- );
135
- } else {
136
- // 防御:结束日期不能早于开始日期
137
- const finalEnd = val < startDate ? startDate : val;
138
- super.changeValueEx(this._rangePicker2Data([moment(startDate), moment(finalEnd)], false), true, true);
139
- }
150
+ // 防御:结束日期不能早于开始日期
151
+ const finalEnd = val < startDate ? startDate : val;
152
+ super.changeValueEx(this._rangePicker2Data([moment(startDate), moment(finalEnd)], false), true, true);
140
153
  }
141
154
  this.setState({ mobileDlg: false });
142
155
  }}
@@ -168,13 +181,20 @@ export class ARangePicker extends Viewer<State> {
168
181
  key={`${this.state.ctrlVersion}_${this.props.schema.dateRangePrecision ?? 'day'}`}
169
182
  renderExtraFooter={hideFooter
170
183
  ? undefined
171
- : (mode) => <div style={{ textAlign: "right" }}>
172
- <AntButton
173
- size="small" style={{ width: "100px", display: "inline-block", marginTop: "5px" }}
174
- onClick={() => {
175
- super.changeValueEx(this._rangePicker2Data(this._onCalendarChangeValue, true), true, true);
176
- }}>至今</AntButton>
177
- </div>
184
+ : (mode) => {
185
+ // 如果开始时间超过当前时间(未来时间),不展示"至今"按钮
186
+ const startMoment = this._onCalendarChangeValue?.[0];
187
+ if (startMoment && startMoment.isAfter(moment())) {
188
+ return null;
189
+ }
190
+ return <div style={{ textAlign: "right" }}>
191
+ <AntButton
192
+ size="small" style={{ width: "100px", display: "inline-block", marginTop: "5px" }}
193
+ onClick={() => {
194
+ super.changeValueEx(this._rangePicker2Data(this._onCalendarChangeValue, true), true, true);
195
+ }}>至今</AntButton>
196
+ </div>;
197
+ }
178
198
  }
179
199
  bordered={this.props.hideBorder ? false : true}
180
200
  style={{ width: "300px" }}
@@ -115,6 +115,20 @@ export declare let MUtil: {
115
115
  renameKey: (object: any, renameTo: {
116
116
  [oldKey: string]: string;
117
117
  }, removeNotExistKey?: boolean) => any;
118
+ /**
119
+ * 提交时将时间字段转换为可读格式
120
+ * @param schema 表单 schema(type 为 object 的根 schema)
121
+ * @param database 表单数据
122
+ * @returns 深拷贝后转换过的数据
123
+ */
124
+ formatForExport: (schema: MFieldSchemaAnonymity, database: any) => any;
125
+ /**
126
+ * 回填时将可读格式的时间字段反解析为内部格式
127
+ * @param schema 表单 schema(type 为 object 的根 schema)
128
+ * @param database 可读格式的数据
129
+ * @returns 深拷贝后反解析过的数据
130
+ */
131
+ parseFromExport: (schema: MFieldSchemaAnonymity, database: any) => any;
118
132
  /** 啥也不干的空回调 */
119
133
  doNothing: () => void;
120
134
  /**
@@ -1,6 +1,8 @@
1
1
  import { MFieldSchemaAnonymity } from "../framework/Schema";
2
2
  import { MType } from "./MType";
3
3
  import { Assembly } from "../framework/Assembly";
4
+ /** 根据 precision 获取可读格式 */
5
+ export declare function getReadableFormat(precision?: string, showTime?: boolean): string;
4
6
  export declare function timeRangeExpr(assembly: Assembly, from: number | string, to: number | string, tillNow: boolean, readableFormat?: string): string;
5
7
  export declare const MDateRangeType: MType & {
6
8
  toReadableN: (assembly: Assembly, s: MFieldSchemaAnonymity, vs: any) => string | undefined;