form-driver 0.4.26 → 0.4.28

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,17 +1,16 @@
1
1
  import React, { Component, RefObject } from "react";
2
2
  import { DatePicker } from "antd";
3
- import zhCN from 'antd/lib/date-picker/locale/zh_CN';
3
+ import zhCN from "antd/lib/date-picker/locale/zh_CN";
4
4
  import moment from "moment";
5
5
  import { DatePicker as DatePickerM } from "antd-mobile";
6
6
  import { Button as AntButton } from "antd";
7
- import ReactDOM from "react-dom";
8
7
  import { RangePickerProps } from "antd/lib/date-picker";
9
8
  import _ from "lodash";
10
- import { Viewer, ViewerState } from '../../BaseViewer';
11
- import { MUtil } from '../../../framework/MUtil';
9
+ import { Viewer, ViewerState } from "../../BaseViewer";
10
+ import { MUtil } from "../../../framework/MUtil";
12
11
  import { MProp } from "../../../framework/Schema";
13
- import { MDateRangeType } from '../../../types/MDateRangeType';
14
- import { assembly } from '../../../framework/Assembly';
12
+ import { MDateRangeType } from "../../../types/MDateRangeType";
13
+ import { assembly } from "../../../framework/Assembly";
15
14
 
16
15
  export type ARangePickerData = [
17
16
  // 开始时间
@@ -19,26 +18,31 @@ export type ARangePickerData = [
19
18
  // 结束时间
20
19
  string | null | undefined,
21
20
  // 是否至今,如果true,结束时间是无效的
22
- boolean | null | undefined
21
+ boolean | null | undefined,
23
22
  ];
24
23
 
25
24
  type AntData = [moment.Moment | null, moment.Moment | null];
26
25
 
27
26
  interface State extends ViewerState {
28
27
  mobileDlg: boolean;
29
- mobileStep: 'start' | 'end';
28
+ mobileStep: "start" | "end";
30
29
  mobileStartDate?: Date;
31
30
  }
32
31
 
33
32
  export class ARangePicker extends Viewer<State> {
34
- _pickerRef: RefObject<any> = React.createRef();
33
+ _pickerRef: RefObject<HTMLDivElement> = React.createRef();
35
34
  _onCalendarChangeValue?: AntData | null;
36
35
  _panelClickedDate?: moment.Moment | null; // showTime 模式下通过面板点击事件捕获的日期
37
36
  _startConfirmed = false; // 标记开始日期是否已确认,用于区分 onClose 是取消还是确认后的自动触发
38
37
 
39
38
  constructor(p: MProp) {
40
39
  super(p);
41
- this.state = { ctrlVersion: 1, noValidate: false, mobileDlg: false, mobileStep: 'start' };
40
+ this.state = {
41
+ ctrlVersion: 1,
42
+ noValidate: false,
43
+ mobileDlg: false,
44
+ mobileStep: "start",
45
+ };
42
46
  }
43
47
 
44
48
  componentDidUpdate() {
@@ -51,13 +55,14 @@ export class ARangePicker extends Viewer<State> {
51
55
 
52
56
  _patchTillnow() {
53
57
  const v = super.getValue();
54
- if (_.get(v, "[2]")) { // tillnow
55
- const dom = ReactDOM.findDOMNode(this._pickerRef.current);
58
+ if (_.get(v, "[2]")) {
59
+ // tillnow
60
+ const dom = this._pickerRef.current;
56
61
  if (dom) {
57
- // @ts-ignore
58
62
  let r = dom.querySelector(":nth-child(3)");
59
63
  if (r) {
60
- r.innerHTML = "<input readonly disabled size='12' autocomplete='off' value='至今' style='color: black'>";
64
+ r.innerHTML =
65
+ "<input readonly disabled size='12' autocomplete='off' value='至今' style='color: black'>";
61
66
  }
62
67
  }
63
68
  }
@@ -65,9 +70,12 @@ export class ARangePicker extends Viewer<State> {
65
70
 
66
71
  /**
67
72
  * RangePicker的数据转换成json上的数据类型
68
- * @param r
73
+ * @param r
69
74
  */
70
- _rangePicker2Data(v: AntData | null | undefined, tillNow: boolean): ARangePickerData | undefined {
75
+ _rangePicker2Data(
76
+ v: AntData | null | undefined,
77
+ tillNow: boolean,
78
+ ): ARangePickerData | undefined {
71
79
  if (!v) {
72
80
  return undefined;
73
81
  }
@@ -77,12 +85,13 @@ export class ARangePicker extends Viewer<State> {
77
85
 
78
86
  /**
79
87
  * json上的数据转换成RangePicker的数据
80
- * @param d
88
+ * @param d
81
89
  */
82
90
  _data2rangePicker(d: ARangePickerData): AntData {
83
91
  const dataFormat = this.props.schema.dataFormat ?? "x";
84
92
  // 若 tillNow=true,结束时间传 null,避免 RangePicker 缓存旧结束时间
85
- const endTime = d[2] === true ? null : (d[1] ? moment(d[1], dataFormat) : null);
93
+ const endTime =
94
+ d[2] === true ? null : d[1] ? moment(d[1], dataFormat) : null;
86
95
  return [d[0] ? moment(d[0], dataFormat) : null, endTime];
87
96
  }
88
97
 
@@ -90,44 +99,76 @@ export class ARangePicker extends Viewer<State> {
90
99
  const p = this.props.schema.props ?? {};
91
100
  let rangePickerData = this._data2rangePicker(this.getValue() ?? []);
92
101
  if (MUtil.phoneLike()) {
93
- let show = MDateRangeType.toReadableN(assembly, this.props.schema, super.getValue());
102
+ let show = MDateRangeType.toReadableN(
103
+ assembly,
104
+ this.props.schema,
105
+ super.getValue(),
106
+ );
94
107
 
95
108
  // 根据 precision 配置确定移动端 DatePicker 精度(使用扁平化属性 dateRangePrecision)
96
- const mobilePrecision = (this.props.schema.dateRangePrecision || 'day') as 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second';
109
+ const mobilePrecision = (this.props.schema.dateRangePrecision ||
110
+ "day") as "year" | "month" | "day" | "hour" | "minute" | "second";
97
111
 
98
- return <>
99
- <div className="backfill" onClick={() => this.setState({ mobileDlg: true, mobileStep: 'start' })}> {show ?? '请点击选择'} </div>
100
- {/* 移动端:使用两步 DatePickerM 选择开始和结束日期 */}
101
- <DatePickerM
102
- key={`start_${mobilePrecision}`}
103
- visible={this.state.mobileDlg && this.state.mobileStep === 'start'}
104
- precision={mobilePrecision}
105
- title="选择开始日期"
106
- min={this.props.schema.min ? new Date(this.props.schema.min) : undefined}
107
- max={this.props.schema.max ? new Date(this.props.schema.max) : undefined}
108
- onConfirm={(val) => {
109
- this._startConfirmed = true;
110
- this.setState({ mobileStartDate: val, mobileStep: 'end' });
111
- }}
112
- onClose={() => {
113
- // antd-mobile v5 确认时会同时触发 onConfirm 和 onClose
114
- // 用实例变量同步判断:确认后的 onClose 应忽略,仅用户主动取消时才关闭
115
- if (this._startConfirmed) {
116
- this._startConfirmed = false;
117
- return;
112
+ return (
113
+ <>
114
+ <div
115
+ className="backfill"
116
+ onClick={() =>
117
+ this.setState({ mobileDlg: true, mobileStep: "start" })
118
118
  }
119
- this.setState({ mobileDlg: false });
120
- }}
121
- />
122
- <DatePickerM
123
- key={`end_${mobilePrecision}`}
124
- visible={this.state.mobileDlg && this.state.mobileStep === 'end'}
125
- precision={mobilePrecision}
126
- title={
127
- // 如果允许"至今"且开始时间不在未来,在标题区域展示"至今"按钮
128
- !this.props.schema.dateRange?.hideTillNow
129
- && !(this.state.mobileStartDate && this.state.mobileStartDate > new Date())
130
- ? <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
119
+ >
120
+ {" "}
121
+ {show ?? "请点击选择"}{" "}
122
+ </div>
123
+ {/* 移动端:使用两步 DatePickerM 选择开始和结束日期 */}
124
+ <DatePickerM
125
+ key={`start_${mobilePrecision}`}
126
+ visible={this.state.mobileDlg && this.state.mobileStep === "start"}
127
+ precision={mobilePrecision}
128
+ title="选择开始日期"
129
+ min={
130
+ this.props.schema.min
131
+ ? new Date(this.props.schema.min)
132
+ : undefined
133
+ }
134
+ max={
135
+ this.props.schema.max
136
+ ? new Date(this.props.schema.max)
137
+ : undefined
138
+ }
139
+ onConfirm={(val) => {
140
+ this._startConfirmed = true;
141
+ this.setState({ mobileStartDate: val, mobileStep: "end" });
142
+ }}
143
+ onClose={() => {
144
+ // antd-mobile v5 确认时会同时触发 onConfirm 和 onClose
145
+ // 用实例变量同步判断:确认后的 onClose 应忽略,仅用户主动取消时才关闭
146
+ if (this._startConfirmed) {
147
+ this._startConfirmed = false;
148
+ return;
149
+ }
150
+ this.setState({ mobileDlg: false });
151
+ }}
152
+ />
153
+ <DatePickerM
154
+ key={`end_${mobilePrecision}`}
155
+ visible={this.state.mobileDlg && this.state.mobileStep === "end"}
156
+ precision={mobilePrecision}
157
+ title={
158
+ // 如果允许"至今"且开始时间不在未来,在标题区域展示"至今"按钮
159
+ !this.props.schema.dateRange?.hideTillNow &&
160
+ !(
161
+ this.state.mobileStartDate &&
162
+ this.state.mobileStartDate > new Date()
163
+ ) ? (
164
+ <div
165
+ style={{
166
+ display: "flex",
167
+ alignItems: "center",
168
+ justifyContent: "center",
169
+ gap: 12,
170
+ }}
171
+ >
131
172
  <span>选择结束日期</span>
132
173
  <AntButton
133
174
  size="small"
@@ -136,51 +177,78 @@ export class ARangePicker extends Viewer<State> {
136
177
  const startDate = this.state.mobileStartDate;
137
178
  if (startDate) {
138
179
  super.changeValueEx(
139
- this._rangePicker2Data([moment(startDate), moment()], true),
140
- true, true
180
+ this._rangePicker2Data(
181
+ [moment(startDate), moment()],
182
+ true,
183
+ ),
184
+ true,
185
+ true,
141
186
  );
142
187
  }
143
188
  this.setState({ mobileDlg: false });
144
189
  }}
145
- >至今</AntButton>
190
+ >
191
+ 至今
192
+ </AntButton>
146
193
  </div>
147
- : "选择结束日期"
148
- }
149
- min={this.state.mobileStartDate || (this.props.schema.min ? new Date(this.props.schema.min) : undefined)}
150
- max={this.props.schema.max ? new Date(this.props.schema.max) : undefined}
151
- onConfirm={(val: any) => {
152
- const startDate = this.state.mobileStartDate;
153
- if (startDate) {
154
- // 防御:结束日期不能早于开始日期
155
- const finalEnd = val < startDate ? startDate : val;
156
- super.changeValueEx(this._rangePicker2Data([moment(startDate), moment(finalEnd)], false), true, true);
194
+ ) : (
195
+ "选择结束日期"
196
+ )
157
197
  }
158
- this.setState({ mobileDlg: false });
159
- }}
160
- onClose={() => {
161
- // 回退到第一步,让用户可以重新选择开始日期
162
- this.setState({ mobileStep: 'start' });
163
- }}
164
- />
165
- </>
198
+ min={
199
+ this.state.mobileStartDate ||
200
+ (this.props.schema.min
201
+ ? new Date(this.props.schema.min)
202
+ : undefined)
203
+ }
204
+ max={
205
+ this.props.schema.max
206
+ ? new Date(this.props.schema.max)
207
+ : undefined
208
+ }
209
+ onConfirm={(val: any) => {
210
+ const startDate = this.state.mobileStartDate;
211
+ if (startDate) {
212
+ // 防御:结束日期不能早于开始日期
213
+ const finalEnd = val < startDate ? startDate : val;
214
+ super.changeValueEx(
215
+ this._rangePicker2Data(
216
+ [moment(startDate), moment(finalEnd)],
217
+ false,
218
+ ),
219
+ true,
220
+ true,
221
+ );
222
+ }
223
+ this.setState({ mobileDlg: false });
224
+ }}
225
+ onClose={() => {
226
+ // 回退到第一步,让用户可以重新选择开始日期
227
+ this.setState({ mobileStep: "start" });
228
+ }}
229
+ />
230
+ </>
231
+ );
166
232
  } else {
167
233
  // 根据 precision 配置确定 PC 端 picker 模式和 showTime(使用扁平化属性 dateRangePrecision)
168
234
  const precision = this.props.schema.dateRangePrecision;
169
- const pcShowTime = precision === 'minute' || this.props.schema.dateRange?.showTime;
235
+ const pcShowTime =
236
+ precision === "minute" || this.props.schema.dateRange?.showTime;
170
237
  const hideFooter = this.props.schema.dateRange?.hideTillNow;
171
238
 
172
239
  // 动态构建额外属性,避免 showTime 和 picker 同时传入导致类型冲突
173
240
  const extraProps: any = {};
174
- if (precision === 'year') {
175
- extraProps.picker = 'year';
176
- } else if (precision === 'month') {
177
- extraProps.picker = 'month';
241
+ if (precision === "year") {
242
+ extraProps.picker = "year";
243
+ } else if (precision === "month") {
244
+ extraProps.picker = "month";
178
245
  } else if (pcShowTime) {
179
246
  // precision 为 minute 时只展示时分,不展示秒
180
- extraProps.showTime = precision === 'minute' ? { format: 'HH:mm' } : true;
247
+ extraProps.showTime =
248
+ precision === "minute" ? { format: "HH:mm" } : true;
181
249
  // 同步设置输入框的显示格式,避免 showTime.format 只影响面板列而输入框仍显示秒
182
- if (precision === 'minute') {
183
- extraProps.format = 'YYYY-MM-DD HH:mm';
250
+ if (precision === "minute") {
251
+ extraProps.format = "YYYY-MM-DD HH:mm";
184
252
  }
185
253
  }
186
254
 
@@ -189,17 +257,26 @@ export class ARangePicker extends Viewer<State> {
189
257
  // 优先级:onCalendarChange 记录的值 > 面板点击捕获的日期 > 已有默认值 > 当前时间
190
258
  const fromCalendarChange = this._onCalendarChangeValue?.[0];
191
259
  const fromPanelClick = this._panelClickedDate;
192
- let startMoment = fromCalendarChange ?? fromPanelClick ?? rangePickerData?.[0] ?? moment();
260
+ let startMoment =
261
+ fromCalendarChange ??
262
+ fromPanelClick ??
263
+ rangePickerData?.[0] ??
264
+ moment();
193
265
 
194
266
  // 如果开始时间来自面板点击(只有日期没有时间),将当前时刻的时分附加上去
195
267
  if (!fromCalendarChange && fromPanelClick) {
196
268
  const now = moment();
197
- startMoment = startMoment.clone().hour(now.hour()).minute(now.minute()).second(0);
269
+ startMoment = startMoment
270
+ .clone()
271
+ .hour(now.hour())
272
+ .minute(now.minute())
273
+ .second(0);
198
274
  }
199
275
 
200
276
  super.changeValueEx(
201
277
  this._rangePicker2Data([startMoment, moment()], true),
202
- true, true
278
+ true,
279
+ true,
203
280
  );
204
281
  };
205
282
 
@@ -207,106 +284,145 @@ export class ARangePicker extends Viewer<State> {
207
284
  const showTillNow = !hideFooter;
208
285
 
209
286
  // 构造元素
210
- return <DatePicker.RangePicker
211
- ref={this._pickerRef}
212
- key={`${this.state.ctrlVersion}_${this.props.schema.dateRangePrecision ?? 'day'}`}
213
- panelRender={pcShowTime && showTillNow
214
- ? (panelNode) => {
215
- // showTime 模式下:通过事件委托捕获面板上日期单元格的点击
216
- // 同时在 footer 的"确定"按钮同一行注入"至今"按钮
217
- return <div onClick={(e) => {
218
- const target = e.target as HTMLElement;
219
- const cell = target.closest?.('.ant-picker-cell');
220
- if (cell) {
221
- // 排除 disabled 状态的日期单元格
222
- if (cell.classList.contains('ant-picker-cell-disabled')) return;
223
- const title = cell.getAttribute('title');
224
- if (title) {
225
- const parsed = moment(title, 'YYYY-MM-DD');
226
- // 校验 moment 解析有效性,无效日期不记录
227
- if (parsed.isValid()) {
228
- this._panelClickedDate = parsed;
287
+ return (
288
+ <div ref={this._pickerRef}>
289
+ <DatePicker.RangePicker
290
+ key={`${this.state.ctrlVersion}_${
291
+ this.props.schema.dateRangePrecision ?? "day"
292
+ }`}
293
+ panelRender={
294
+ pcShowTime && showTillNow
295
+ ? (panelNode) => {
296
+ // showTime 模式下:通过事件委托捕获面板上日期单元格的点击
297
+ // 同时在 footer 的"确定"按钮同一行注入"至今"按钮
298
+ return (
299
+ <div
300
+ onClick={(e) => {
301
+ const target = e.target as HTMLElement;
302
+ const cell = target.closest?.(".ant-picker-cell");
303
+ if (cell) {
304
+ // 排除 disabled 状态的日期单元格
305
+ if (
306
+ cell.classList.contains(
307
+ "ant-picker-cell-disabled",
308
+ )
309
+ )
310
+ return;
311
+ const title = cell.getAttribute("title");
312
+ if (title) {
313
+ const parsed = moment(title, "YYYY-MM-DD");
314
+ // 校验 moment 解析有效性,无效日期不记录
315
+ if (parsed.isValid()) {
316
+ this._panelClickedDate = parsed;
317
+ }
318
+ }
319
+ }
320
+ }}
321
+ ref={(el) => {
322
+ // 面板渲染后,将"至今"按钮注入到 .ant-picker-ok 内部,确定按钮之后
323
+ // 这样两者在同一个 flex item 中,确定在左、至今在右
324
+ if (!el) return;
325
+ const okLi = el.querySelector(".ant-picker-ok");
326
+ if (!okLi) return;
327
+
328
+ // 开始时间在未来时,移除已有的至今按钮并不再注入
329
+ const previewStart =
330
+ this._onCalendarChangeValue?.[0] ??
331
+ this._panelClickedDate ??
332
+ rangePickerData?.[0];
333
+ if (previewStart && previewStart.isAfter(moment())) {
334
+ const existingBtn =
335
+ okLi.querySelector(".till-now-btn");
336
+ if (existingBtn) existingBtn.remove();
337
+ return;
338
+ }
339
+
340
+ if (!okLi.querySelector(".till-now-btn")) {
341
+ const tillNowBtn = document.createElement("button");
342
+ tillNowBtn.className =
343
+ "ant-btn ant-btn-sm till-now-btn";
344
+ tillNowBtn.textContent = "至今";
345
+ tillNowBtn.style.cssText = "margin-left: 8px;";
346
+ // 使用具名函数以便于清理;先移除可能的旧监听再添加,防止重复绑定
347
+ const onTillNowClick = (e: Event) => {
348
+ e.stopPropagation();
349
+ handleTillNow();
350
+ };
351
+ tillNowBtn.addEventListener(
352
+ "click",
353
+ onTillNowClick,
354
+ );
355
+ okLi.appendChild(tillNowBtn);
356
+ }
357
+ }}
358
+ >
359
+ {panelNode}
360
+ </div>
361
+ );
362
+ }
363
+ : undefined
364
+ }
365
+ renderExtraFooter={
366
+ !pcShowTime && showTillNow
367
+ ? (mode) => {
368
+ // 非 showTime 模式:使用 renderExtraFooter 展示"至今"按钮
369
+ const previewStart =
370
+ this._onCalendarChangeValue?.[0] ?? rangePickerData?.[0];
371
+ if (previewStart && previewStart.isAfter(moment())) {
372
+ return null;
373
+ }
374
+ return (
375
+ <div style={{ textAlign: "right" }}>
376
+ <AntButton
377
+ size="small"
378
+ style={{
379
+ width: "100px",
380
+ display: "inline-block",
381
+ marginTop: "5px",
382
+ }}
383
+ onClick={handleTillNow}
384
+ >
385
+ 至今
386
+ </AntButton>
387
+ </div>
388
+ );
229
389
  }
230
- }
390
+ : undefined
391
+ }
392
+ bordered={this.props.hideBorder ? false : true}
393
+ style={{ width: "100%" }}
394
+ locale={zhCN}
395
+ defaultValue={rangePickerData}
396
+ onCalendarChange={(d) => {
397
+ this._onCalendarChangeValue = d;
398
+ // 用户开始新一轮选择时清理面板点击缓存,避免上一轮过时数据污染
399
+ this._panelClickedDate = null;
400
+ }}
401
+ onOpenChange={(open) => {
402
+ if (!open) {
403
+ this._panelClickedDate = null;
231
404
  }
232
405
  }}
233
- ref={(el) => {
234
- // 面板渲染后,将"至今"按钮注入到 .ant-picker-ok 内部,确定按钮之后
235
- // 这样两者在同一个 flex item 中,确定在左、至今在右
236
- if (!el) return;
237
- const okLi = el.querySelector('.ant-picker-ok');
238
- if (!okLi) return;
239
-
240
- // 开始时间在未来时,移除已有的至今按钮并不再注入
241
- const previewStart = this._onCalendarChangeValue?.[0] ?? this._panelClickedDate ?? rangePickerData?.[0];
242
- if (previewStart && previewStart.isAfter(moment())) {
243
- const existingBtn = okLi.querySelector('.till-now-btn');
244
- if (existingBtn) existingBtn.remove();
406
+ onChange={(vv) => {
407
+ // 用户清空日期范围时,直接置空
408
+ if (!vv) {
409
+ super.changeValueEx(undefined, true, true);
245
410
  return;
246
411
  }
247
-
248
- if (!okLi.querySelector('.till-now-btn')) {
249
- const tillNowBtn = document.createElement('button');
250
- tillNowBtn.className = 'ant-btn ant-btn-sm till-now-btn';
251
- tillNowBtn.textContent = '至今';
252
- tillNowBtn.style.cssText = 'margin-left: 8px;';
253
- // 使用具名函数以便于清理;先移除可能的旧监听再添加,防止重复绑定
254
- const onTillNowClick = (e: Event) => {
255
- e.stopPropagation();
256
- handleTillNow();
257
- };
258
- tillNowBtn.addEventListener('click', onTillNowClick);
259
- okLi.appendChild(tillNowBtn);
260
- }
261
- }}>
262
- {panelNode}
263
- </div>;
264
- }
265
- : undefined
266
- }
267
- renderExtraFooter={!pcShowTime && showTillNow
268
- ? (mode) => {
269
- // 非 showTime 模式:使用 renderExtraFooter 展示"至今"按钮
270
- const previewStart = this._onCalendarChangeValue?.[0] ?? rangePickerData?.[0];
271
- if (previewStart && previewStart.isAfter(moment())) {
272
- return null;
273
- }
274
- return <div style={{ textAlign: "right" }}>
275
- <AntButton
276
- size="small" style={{ width: "100px", display: "inline-block", marginTop: "5px" }}
277
- onClick={handleTillNow}>至今</AntButton>
278
- </div>;
279
- }
280
- : undefined
281
- }
282
- bordered={this.props.hideBorder ? false : true}
283
- style={{ width: "300px" }}
284
- locale={zhCN}
285
- defaultValue={rangePickerData}
286
- onCalendarChange={(d) => {
287
- this._onCalendarChangeValue = d;
288
- // 用户开始新一轮选择时清理面板点击缓存,避免上一轮过时数据污染
289
- this._panelClickedDate = null;
290
- }}
291
- onOpenChange={(open) => {
292
- if (!open) {
293
- this._panelClickedDate = null;
294
- }
295
- }}
296
- onChange={(vv) => {
297
- // 用户清空日期范围时,直接置空
298
- if (!vv) {
299
- super.changeValueEx(undefined, true, true);
300
- return;
301
- }
302
- const currentData = super.getValue();
303
- const isTillNow = _.get(currentData, '[2]') === true;
304
- // 若当前是"至今"且用户只改了开始时间(结束时间仍为 null),保留 tillNow
305
- const newTillNow = isTillNow && vv[1] == null;
306
- super.changeValueEx(this._rangePicker2Data(vv, newTillNow), true, true);
307
- }}
308
- {...extraProps}
309
- />;
412
+ const currentData = super.getValue();
413
+ const isTillNow = _.get(currentData, "[2]") === true;
414
+ // 若当前是"至今"且用户只改了开始时间(结束时间仍为 null),保留 tillNow
415
+ const newTillNow = isTillNow && vv[1] == null;
416
+ super.changeValueEx(
417
+ this._rangePicker2Data(vv, newTillNow),
418
+ true,
419
+ true,
420
+ );
421
+ }}
422
+ {...extraProps}
423
+ />
424
+ </div>
425
+ );
310
426
  }
311
427
  }
312
428
  }
@@ -1,6 +1,6 @@
1
1
  import { RefObject } from "react";
2
2
  import moment from "moment";
3
- import { Viewer, ViewerState } from '../../BaseViewer';
3
+ import { Viewer, ViewerState } from "../../BaseViewer";
4
4
  import { MProp } from "../../../framework/Schema";
5
5
  export type ARangePickerData = [
6
6
  string | null | undefined,
@@ -10,11 +10,11 @@ export type ARangePickerData = [
10
10
  type AntData = [moment.Moment | null, moment.Moment | null];
11
11
  interface State extends ViewerState {
12
12
  mobileDlg: boolean;
13
- mobileStep: 'start' | 'end';
13
+ mobileStep: "start" | "end";
14
14
  mobileStartDate?: Date;
15
15
  }
16
16
  export declare class ARangePicker extends Viewer<State> {
17
- _pickerRef: RefObject<any>;
17
+ _pickerRef: RefObject<HTMLDivElement>;
18
18
  _onCalendarChangeValue?: AntData | null;
19
19
  _panelClickedDate?: moment.Moment | null;
20
20
  _startConfirmed: boolean;