form-driver 0.4.19 → 0.4.20
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/README.md +15 -0
- package/dist/m3.js +1 -1
- package/es/m3.js +353 -120
- package/lib/m3.js +352 -118
- package/package.json +2 -2
- package/src/framework/Init.tsx +6 -0
- package/src/framework/Schema.ts +16 -2
- package/src/index.ts +0 -1
- package/src/types/MDateRangeType.ts +65 -24
- package/src/types/MDateTimeType.ts +73 -29
- package/src/ui/editor/basic/ACascadePicker.tsx +23 -15
- package/src/ui/editor/basic/ADatetimePicker.tsx +103 -39
- package/src/ui/editor/basic/AGB2260.tsx +25 -14
- package/src/ui/editor/basic/ARangePicker.tsx +86 -22
- package/src/ui/widget/SelectBox.tsx +35 -15
- package/types/framework/Init.d.ts +1 -0
- package/types/framework/Schema.d.ts +11 -0
- package/types/index.d.ts +0 -1
- package/types/types/MDateTimeType.d.ts +2 -2
- package/types/ui/editor/basic/ADatetimePicker.d.ts +2 -1
- package/types/ui/editor/basic/AGB2260.d.ts +1 -0
- package/types/ui/editor/basic/ARangePicker.d.ts +5 -3
- package/types/ui/widget/SelectBox.d.ts +10 -3
|
@@ -2,7 +2,8 @@ import React, { Component, RefObject } from "react";
|
|
|
2
2
|
import { DatePicker } from "antd";
|
|
3
3
|
import zhCN from 'antd/lib/date-picker/locale/zh_CN';
|
|
4
4
|
import moment from "moment";
|
|
5
|
-
import {
|
|
5
|
+
import { DatePicker as DatePickerM } from "antd-mobile";
|
|
6
|
+
import { Button as AntButton } from "antd";
|
|
6
7
|
import ReactDOM from "react-dom";
|
|
7
8
|
import { RangePickerProps } from "antd/lib/date-picker";
|
|
8
9
|
import _ from "lodash";
|
|
@@ -25,15 +26,18 @@ type AntData = [moment.Moment | null, moment.Moment | null];
|
|
|
25
26
|
|
|
26
27
|
interface State extends ViewerState {
|
|
27
28
|
mobileDlg: boolean;
|
|
29
|
+
mobileStep: 'start' | 'end';
|
|
30
|
+
mobileStartDate?: Date;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
export class ARangePicker extends Viewer<State> {
|
|
31
|
-
_pickerRef: RefObject<
|
|
34
|
+
_pickerRef: RefObject<any> = React.createRef();
|
|
32
35
|
_onCalendarChangeValue?: AntData | null;
|
|
36
|
+
_startConfirmed = false; // 标记开始日期是否已确认,用于区分 onClose 是取消还是确认后的自动触发
|
|
33
37
|
|
|
34
38
|
constructor(p: MProp) {
|
|
35
39
|
super(p);
|
|
36
|
-
this.state = { ctrlVersion: 1, noValidate: false, mobileDlg: false };
|
|
40
|
+
this.state = { ctrlVersion: 1, noValidate: false, mobileDlg: false, mobileStep: 'start' };
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
componentDidUpdate() {
|
|
@@ -83,39 +87,97 @@ export class ARangePicker extends Viewer<State> {
|
|
|
83
87
|
if (MUtil.phoneLike()) {
|
|
84
88
|
let show = MDateRangeType.toReadableN(assembly, this.props.schema, super.getValue());
|
|
85
89
|
|
|
90
|
+
// 根据 precision 配置确定移动端 DatePicker 精度(使用扁平化属性 dateRangePrecision)
|
|
91
|
+
const mobilePrecision = (this.props.schema.dateRangePrecision || 'day') as 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second';
|
|
92
|
+
|
|
86
93
|
return <>
|
|
87
|
-
<div className="backfill" onClick={() => this.setState({ mobileDlg: true })}> {show ?? '请点击选择'} </div>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
<div className="backfill" onClick={() => this.setState({ mobileDlg: true, mobileStep: 'start' })}> {show ?? '请点击选择'} </div>
|
|
95
|
+
{/* 移动端:使用两步 DatePickerM 选择开始和结束日期 */}
|
|
96
|
+
<DatePickerM
|
|
97
|
+
key={`start_${mobilePrecision}`}
|
|
98
|
+
visible={this.state.mobileDlg && this.state.mobileStep === 'start'}
|
|
99
|
+
precision={mobilePrecision}
|
|
100
|
+
title="选择开始日期"
|
|
101
|
+
min={this.props.schema.min ? new Date(this.props.schema.min) : undefined}
|
|
102
|
+
max={this.props.schema.max ? new Date(this.props.schema.max) : undefined}
|
|
103
|
+
onConfirm={(val) => {
|
|
104
|
+
this._startConfirmed = true;
|
|
105
|
+
this.setState({ mobileStartDate: val, mobileStep: 'end' });
|
|
106
|
+
}}
|
|
107
|
+
onClose={() => {
|
|
108
|
+
// antd-mobile v5 确认时会同时触发 onConfirm 和 onClose
|
|
109
|
+
// 用实例变量同步判断:确认后的 onClose 应忽略,仅用户主动取消时才关闭
|
|
110
|
+
if (this._startConfirmed) {
|
|
111
|
+
this._startConfirmed = false;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.setState({ mobileDlg: false });
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
<DatePickerM
|
|
118
|
+
key={`end_${mobilePrecision}`}
|
|
119
|
+
visible={this.state.mobileDlg && this.state.mobileStep === 'end'}
|
|
120
|
+
precision={mobilePrecision}
|
|
121
|
+
title="选择结束日期"
|
|
122
|
+
tillNow={!this.props.schema.dateRange?.hideTillNow && !this.props.schema.dateRange?.showTime}
|
|
123
|
+
min={this.state.mobileStartDate || (this.props.schema.min ? new Date(this.props.schema.min) : undefined)}
|
|
124
|
+
max={this.props.schema.max ? new Date(this.props.schema.max) : undefined}
|
|
125
|
+
onConfirm={(val: any) => {
|
|
126
|
+
const startDate = this.state.mobileStartDate;
|
|
127
|
+
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
|
+
}
|
|
140
|
+
}
|
|
141
|
+
this.setState({ mobileDlg: false });
|
|
142
|
+
}}
|
|
143
|
+
onClose={() => {
|
|
144
|
+
// 回退到第一步,让用户可以重新选择开始日期
|
|
145
|
+
this.setState({ mobileStep: 'start' });
|
|
97
146
|
}}
|
|
98
|
-
{...p}
|
|
99
147
|
/>
|
|
100
148
|
</>
|
|
101
149
|
} else {
|
|
150
|
+
// 根据 precision 配置确定 PC 端 picker 模式和 showTime(使用扁平化属性 dateRangePrecision)
|
|
151
|
+
const precision = this.props.schema.dateRangePrecision;
|
|
152
|
+
const pcShowTime = precision === 'minute' || this.props.schema.dateRange?.showTime;
|
|
153
|
+
const hideFooter = this.props.schema.dateRange?.hideTillNow || pcShowTime;
|
|
154
|
+
|
|
155
|
+
// 动态构建额外属性,避免 showTime 和 picker 同时传入导致类型冲突
|
|
156
|
+
const extraProps: any = {};
|
|
157
|
+
if (precision === 'year') {
|
|
158
|
+
extraProps.picker = 'year';
|
|
159
|
+
} else if (precision === 'month') {
|
|
160
|
+
extraProps.picker = 'month';
|
|
161
|
+
} else if (pcShowTime) {
|
|
162
|
+
extraProps.showTime = true;
|
|
163
|
+
}
|
|
164
|
+
|
|
102
165
|
// 构造元素
|
|
103
166
|
return <DatePicker.RangePicker
|
|
104
167
|
ref={this._pickerRef}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
renderExtraFooter={this.props.schema.dateRange?.hideTillNow || this.props.schema.dateRange?.showTime // TODO 显示时间时,“至今”无法支持
|
|
168
|
+
key={`${this.state.ctrlVersion}_${this.props.schema.dateRangePrecision ?? 'day'}`}
|
|
169
|
+
renderExtraFooter={hideFooter
|
|
108
170
|
? undefined
|
|
109
|
-
: (mode) => <div style={{ textAlign: "right" }}
|
|
110
|
-
<
|
|
171
|
+
: (mode) => <div style={{ textAlign: "right" }}>
|
|
172
|
+
<AntButton
|
|
111
173
|
size="small" style={{ width: "100px", display: "inline-block", marginTop: "5px" }}
|
|
112
174
|
onClick={() => {
|
|
113
175
|
super.changeValueEx(this._rangePicker2Data(this._onCalendarChangeValue, true), true, true);
|
|
114
|
-
}}>至今</
|
|
176
|
+
}}>至今</AntButton>
|
|
115
177
|
</div>
|
|
116
178
|
}
|
|
117
179
|
bordered={this.props.hideBorder ? false : true}
|
|
118
|
-
style={{
|
|
180
|
+
style={{ width: "300px" }}
|
|
119
181
|
locale={zhCN}
|
|
120
182
|
defaultValue={rangePickerData}
|
|
121
183
|
onCalendarChange={(d) => {
|
|
@@ -123,7 +185,9 @@ export class ARangePicker extends Viewer<State> {
|
|
|
123
185
|
}}
|
|
124
186
|
onChange={(vv) => {
|
|
125
187
|
super.changeValueEx(this._rangePicker2Data(vv, false), true, true)
|
|
126
|
-
}}
|
|
188
|
+
}}
|
|
189
|
+
{...extraProps}
|
|
190
|
+
/>;
|
|
127
191
|
}
|
|
128
192
|
}
|
|
129
193
|
}
|
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import { Select } from "antd";
|
|
4
4
|
import { Picker } from "antd-mobile";
|
|
5
|
-
import { PickerData } from "antd-mobile/lib/picker/PropsType";
|
|
6
5
|
import _ from "lodash";
|
|
7
6
|
import React from "react";
|
|
8
|
-
//import { InputHTMLAttributes } from "react";
|
|
9
7
|
import { MUtil } from '../../framework/MUtil';
|
|
10
8
|
|
|
11
|
-
interface
|
|
9
|
+
interface PickerOption {
|
|
10
|
+
label: string | React.ReactNode;
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Prop {
|
|
12
15
|
data: string;
|
|
13
|
-
options:
|
|
16
|
+
options: PickerOption[],
|
|
14
17
|
onChange: (newValue?:string)=>void;
|
|
15
18
|
onBlur?:()=>void;
|
|
16
19
|
|
|
@@ -18,22 +21,39 @@ interface Prop { // extends InputHTMLAttributes<HTMLInputElement> {
|
|
|
18
21
|
openLabel?: string;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
interface SelectBoxState {
|
|
25
|
+
pickerVisible: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class SelectBox extends React.Component<Prop, SelectBoxState> {
|
|
29
|
+
constructor(props: Prop) {
|
|
30
|
+
super(props);
|
|
31
|
+
this.state = { pickerVisible: false };
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
render(){
|
|
23
35
|
if(MUtil.phoneLike()) {
|
|
24
36
|
const looked = this.props.options.find(o=>o.value === this.props.data);
|
|
25
37
|
const backfillClass = looked ? "backfill" : "backfill_empty";
|
|
26
38
|
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
return <>
|
|
40
|
+
<div className={backfillClass} onClick={() => this.setState({ pickerVisible: true })}>
|
|
41
|
+
{looked?.label ?? "点击选择"}
|
|
42
|
+
</div>
|
|
43
|
+
<Picker
|
|
44
|
+
columns={[this.props.options]}
|
|
45
|
+
value={[this.props.data]}
|
|
46
|
+
visible={this.state.pickerVisible}
|
|
47
|
+
onConfirm={e => {
|
|
48
|
+
this.props.onChange(_.last(e) as string);
|
|
49
|
+
this.setState({ pickerVisible: false });
|
|
50
|
+
}}
|
|
51
|
+
onClose={() => {
|
|
52
|
+
this.setState({ pickerVisible: false });
|
|
53
|
+
if(this.props.onBlur) { this.props.onBlur() }
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
</>
|
|
37
57
|
} else {
|
|
38
58
|
return <Select defaultValue={this.props.data}>
|
|
39
59
|
{this.props.options.map(o=><Select.Option value={o.value}>{o.label ?? o.value}</Select.Option>)}
|
|
@@ -132,13 +132,24 @@ export interface MFieldSchema {
|
|
|
132
132
|
/** 允许时间段重叠,默认是不能重叠 */
|
|
133
133
|
overlap?: boolean;
|
|
134
134
|
};
|
|
135
|
+
/** date 类型配置 */
|
|
136
|
+
date?: {
|
|
137
|
+
/** 日期精度:month=年月, day=年月日(默认), minute=年月日时分 */
|
|
138
|
+
precision?: "month" | "day" | "minute";
|
|
139
|
+
};
|
|
140
|
+
/** 日期精度(扁平化属性):year=年, month=年月, day=年月日(默认), minute=年月日时分 */
|
|
141
|
+
datePrecision?: string;
|
|
135
142
|
/** dateRange 类型配置 */
|
|
136
143
|
dateRange?: {
|
|
137
144
|
/** 是否隐藏至今按钮 */
|
|
138
145
|
hideTillNow?: boolean;
|
|
139
146
|
/** 是否能选择时间 */
|
|
140
147
|
showTime?: boolean;
|
|
148
|
+
/** 日期精度:month=年月, day=年月日(默认), minute=年月日时分 */
|
|
149
|
+
precision?: "month" | "day" | "minute";
|
|
141
150
|
};
|
|
151
|
+
/** 日期范围精度(扁平化属性) */
|
|
152
|
+
dateRangePrecision?: string;
|
|
142
153
|
/** 数据格式 */
|
|
143
154
|
dataFormat?: "x" | "YYYYMMDD" /** 用于时间日期类型字段的数据格式,参考moment,例如x表示数据是时间戳,YYYYMMDD表示数据是形如19990130的字符串 */ | string;
|
|
144
155
|
/**
|
package/types/index.d.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,9 +1,9 @@
|
|
|
1
|
-
import { MFieldSchemaAnonymity } from
|
|
1
|
+
import { MFieldSchemaAnonymity } from "../framework/Schema";
|
|
2
2
|
import { MType } from "./MType";
|
|
3
3
|
export interface MDateTimeAntConf {
|
|
4
4
|
dataFormat: string;
|
|
5
5
|
readableFormat: string;
|
|
6
|
-
mode: undefined |
|
|
6
|
+
mode: undefined | "date" | "year" | "month" | "time";
|
|
7
7
|
showTime: boolean;
|
|
8
8
|
}
|
|
9
9
|
export declare const MDateTimeType: MType & {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { BaseViewer } from
|
|
2
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
3
3
|
/**
|
|
4
4
|
* 日期选择框
|
|
5
5
|
* 配置示例:
|
|
@@ -11,5 +11,6 @@ import { BaseViewer } from '../../BaseViewer';
|
|
|
11
11
|
* 类型是dateTime时,dataFormat默认是x,例如1608897466955
|
|
12
12
|
*/
|
|
13
13
|
export declare class ADatetimePicker extends BaseViewer {
|
|
14
|
+
constructor(props: any);
|
|
14
15
|
element(): JSX.Element;
|
|
15
16
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RefObject } from "react";
|
|
2
2
|
import moment from "moment";
|
|
3
|
-
import { RangePickerProps } from "antd/lib/date-picker";
|
|
4
3
|
import { Viewer, ViewerState } from '../../BaseViewer';
|
|
5
4
|
import { MProp } from "../../../framework/Schema";
|
|
6
5
|
export type ARangePickerData = [
|
|
@@ -11,10 +10,13 @@ export type ARangePickerData = [
|
|
|
11
10
|
type AntData = [moment.Moment | null, moment.Moment | null];
|
|
12
11
|
interface State extends ViewerState {
|
|
13
12
|
mobileDlg: boolean;
|
|
13
|
+
mobileStep: 'start' | 'end';
|
|
14
|
+
mobileStartDate?: Date;
|
|
14
15
|
}
|
|
15
16
|
export declare class ARangePicker extends Viewer<State> {
|
|
16
|
-
_pickerRef: RefObject<
|
|
17
|
+
_pickerRef: RefObject<any>;
|
|
17
18
|
_onCalendarChangeValue?: AntData | null;
|
|
19
|
+
_startConfirmed: boolean;
|
|
18
20
|
constructor(p: MProp);
|
|
19
21
|
componentDidUpdate(): void;
|
|
20
22
|
componentDidMount(): void;
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { PickerData } from "antd-mobile/lib/picker/PropsType";
|
|
2
1
|
import React from "react";
|
|
2
|
+
interface PickerOption {
|
|
3
|
+
label: string | React.ReactNode;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
3
6
|
interface Prop {
|
|
4
7
|
data: string;
|
|
5
|
-
options:
|
|
8
|
+
options: PickerOption[];
|
|
6
9
|
onChange: (newValue?: string) => void;
|
|
7
10
|
onBlur?: () => void;
|
|
8
11
|
openLabel?: string;
|
|
9
12
|
}
|
|
10
|
-
|
|
13
|
+
interface SelectBoxState {
|
|
14
|
+
pickerVisible: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class SelectBox extends React.Component<Prop, SelectBoxState> {
|
|
17
|
+
constructor(props: Prop);
|
|
11
18
|
render(): JSX.Element;
|
|
12
19
|
}
|
|
13
20
|
export {};
|