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/dist/m3.js +1 -1
- package/es/m3.js +1090 -878
- package/lib/m3.js +1090 -878
- package/package.json +1 -1
- package/src/framework/MUtil.tsx +145 -0
- package/src/types/MDateRangeType.ts +7 -7
- package/src/types/MDateTimeType.ts +6 -6
- package/src/ui/editor/basic/ARangePicker.tsx +41 -21
- package/types/framework/MUtil.d.ts +14 -0
- package/types/types/MDateRangeType.d.ts +2 -0
package/package.json
CHANGED
package/src/framework/MUtil.tsx
CHANGED
|
@@ -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 "YYYY
|
|
14
|
+
return "YYYY.MM";
|
|
15
15
|
case "minute":
|
|
16
|
-
return "YYYY
|
|
16
|
+
return "YYYY.MM.DD HH:mm";
|
|
17
17
|
default:
|
|
18
|
-
return showTime ? "YYYY
|
|
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 = "YYYY
|
|
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
|
|
34
|
+
readableFormat = "YYYY.MM";
|
|
35
35
|
break;
|
|
36
36
|
case "yearMonthDay":
|
|
37
37
|
mode = "date";
|
|
38
38
|
dataFormat = dataFormat ?? "YYYYMMDD";
|
|
39
|
-
readableFormat = "YYYY
|
|
39
|
+
readableFormat = "YYYY.MM.DD";
|
|
40
40
|
break;
|
|
41
41
|
case "datetime":
|
|
42
42
|
mode = undefined;
|
|
43
43
|
dataFormat = dataFormat ?? "x";
|
|
44
|
-
readableFormat = "YYYY
|
|
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
|
|
57
|
+
readableFormat = "YYYY.MM";
|
|
58
58
|
break;
|
|
59
59
|
case "minute":
|
|
60
60
|
mode = undefined;
|
|
61
61
|
dataFormat = dataFormat ?? "x";
|
|
62
|
-
readableFormat = "YYYY
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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) =>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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;
|