mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 11:07:52 +08:00
feat: input-date 支持手动输入 (#3677)
* feat: input-date 支持手动输入 * update snapshots
This commit is contained in:
parent
ce7ae06c67
commit
a840b910e7
@ -56,11 +56,12 @@ exports[`Renderer:date 1`] = `
|
|||||||
class="cxd-DatePicker"
|
class="cxd-DatePicker"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<input
|
||||||
class="cxd-DatePicker-value"
|
autocomplete="off"
|
||||||
>
|
placeholder="请选择日期以及时间"
|
||||||
2019-06-06 21:11:00
|
type="text"
|
||||||
</span>
|
value="2019-06-06 21:11:00"
|
||||||
|
/>
|
||||||
<a
|
<a
|
||||||
class="cxd-DatePicker-clear"
|
class="cxd-DatePicker-clear"
|
||||||
>
|
>
|
||||||
|
@ -56,11 +56,12 @@ exports[`Renderer:inputDate 1`] = `
|
|||||||
class="cxd-DatePicker"
|
class="cxd-DatePicker"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<input
|
||||||
class="cxd-DatePicker-value"
|
autocomplete="off"
|
||||||
>
|
placeholder="请选择日期"
|
||||||
2019-06-07
|
type="text"
|
||||||
</span>
|
value="2019-06-07"
|
||||||
|
/>
|
||||||
<a
|
<a
|
||||||
class="cxd-DatePicker-clear"
|
class="cxd-DatePicker-clear"
|
||||||
>
|
>
|
||||||
|
@ -31,8 +31,11 @@ test('Renderer:date', async () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = container.querySelector('.cxd-DatePicker-value');
|
const input = container.querySelector(
|
||||||
expect(input?.innerHTML).toEqual(
|
'.cxd-DatePicker input'
|
||||||
|
)! as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(input.value).toEqual(
|
||||||
moment(1559826660, 'X').format('YYYY-MM-DD HH:mm:ss')
|
moment(1559826660, 'X').format('YYYY-MM-DD HH:mm:ss')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,10 +31,10 @@ test('Renderer:inputDate', async () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = container.querySelector('.cxd-DatePicker-value');
|
const input = container.querySelector(
|
||||||
expect(input?.innerHTML).toEqual(
|
'.cxd-DatePicker input'
|
||||||
moment(1559836800, 'X').format('YYYY-MM-DD')
|
)! as HTMLInputElement;
|
||||||
);
|
expect(input.value).toEqual(moment(1559836800, 'X').format('YYYY-MM-DD'));
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -8,27 +8,28 @@ import {makeEnv} from '../../helper';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
test('Renderer:inputMonth click', async () => {
|
test('Renderer:inputMonth click', async () => {
|
||||||
const {container, findByText} = render(
|
const {container, findByText, findByPlaceholderText, findByDisplayValue} =
|
||||||
amisRender(
|
render(
|
||||||
{
|
amisRender(
|
||||||
type: 'form',
|
{
|
||||||
api: '/api/xxx',
|
type: 'form',
|
||||||
body: [
|
api: '/api/xxx',
|
||||||
{
|
body: [
|
||||||
type: 'input-month',
|
{
|
||||||
name: 'month',
|
type: 'input-month',
|
||||||
label: '时间'
|
name: 'month',
|
||||||
}
|
label: '时间'
|
||||||
],
|
}
|
||||||
title: 'The form',
|
],
|
||||||
actions: []
|
title: 'The form',
|
||||||
},
|
actions: []
|
||||||
{},
|
},
|
||||||
makeEnv({})
|
{},
|
||||||
)
|
makeEnv({})
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const inputDate = await findByText('请选择月份');
|
const inputDate = await findByPlaceholderText('请选择月份');
|
||||||
|
|
||||||
fireEvent.click(inputDate);
|
fireEvent.click(inputDate);
|
||||||
|
|
||||||
@ -43,11 +44,11 @@ test('Renderer:inputMonth click', async () => {
|
|||||||
|
|
||||||
const lastYearMonth = moment().subtract(1, 'year').format('YYYY') + '-01';
|
const lastYearMonth = moment().subtract(1, 'year').format('YYYY') + '-01';
|
||||||
|
|
||||||
await findByText(lastYearMonth);
|
await findByDisplayValue(lastYearMonth);
|
||||||
|
|
||||||
const value = document.querySelector(
|
const value = document.querySelector(
|
||||||
'.cxd-DatePicker-value'
|
'.cxd-DatePicker input'
|
||||||
) as HTMLSpanElement;
|
) as HTMLInputElement;
|
||||||
|
|
||||||
expect(value.innerHTML).toEqual(lastYearMonth);
|
expect(value.value).toEqual(lastYearMonth);
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ import {makeEnv} from '../../helper';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
test('Renderer:inputYear click', async () => {
|
test('Renderer:inputYear click', async () => {
|
||||||
const {container, findByText} = render(
|
const {container, findByPlaceholderText, findByText} = render(
|
||||||
amisRender(
|
amisRender(
|
||||||
{
|
{
|
||||||
type: 'form',
|
type: 'form',
|
||||||
@ -28,7 +28,7 @@ test('Renderer:inputYear click', async () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputDate = await findByText('请选择年');
|
const inputDate = await findByPlaceholderText('请选择年');
|
||||||
|
|
||||||
fireEvent.click(inputDate);
|
fireEvent.click(inputDate);
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ test('Renderer:inputYear click', async () => {
|
|||||||
fireEvent.click(thisYear);
|
fireEvent.click(thisYear);
|
||||||
|
|
||||||
const value = document.querySelector(
|
const value = document.querySelector(
|
||||||
'.cxd-DatePicker-value'
|
'.cxd-DatePicker input'
|
||||||
) as HTMLSpanElement;
|
) as HTMLInputElement;
|
||||||
|
|
||||||
expect(value.innerHTML).toEqual(thisYearText);
|
expect(value.value).toEqual(thisYearText);
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@ afterAll(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Renderer:mobile Input Date', async () => {
|
test('Renderer:mobile Input Date', async () => {
|
||||||
const {container, findByText, getByText} = render(
|
const {container, findByPlaceholderText, getByText} = render(
|
||||||
amisRender(
|
amisRender(
|
||||||
{
|
{
|
||||||
type: 'form',
|
type: 'form',
|
||||||
@ -35,7 +35,7 @@ test('Renderer:mobile Input Date', async () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputDate = await findByText('请选择日期');
|
const inputDate = await findByPlaceholderText('请选择日期');
|
||||||
fireEvent.click(inputDate);
|
fireEvent.click(inputDate);
|
||||||
|
|
||||||
const confirmButton = document.querySelector(
|
const confirmButton = document.querySelector(
|
||||||
@ -44,9 +44,9 @@ test('Renderer:mobile Input Date', async () => {
|
|||||||
|
|
||||||
fireEvent.click(confirmButton);
|
fireEvent.click(confirmButton);
|
||||||
|
|
||||||
const value = document.querySelector(
|
// const value = document.querySelector(
|
||||||
'.cxd-DatePicker-value'
|
// '.cxd-DatePicker-value'
|
||||||
) as HTMLSpanElement;
|
// ) as HTMLSpanElement;
|
||||||
|
|
||||||
// TODO: 这里原组件的日错了,等修复
|
// TODO: 这里原组件的日错了,等修复
|
||||||
// expect(value.innerHTML).toEqual(moment().format('YYYY-MM-DD'));
|
// expect(value.innerHTML).toEqual(moment().format('YYYY-MM-DD'));
|
||||||
|
@ -84,20 +84,39 @@ order: 51
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 默认值
|
||||||
|
|
||||||
|
和其它表单项一样,如果要设置默认值,可以使用 value 属性
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"debug": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"name": "switch",
|
||||||
|
"type": "switch",
|
||||||
|
"label": "开关",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ---------- | -------- | --------- | ------------ |
|
| ---------- | --------------------------- | ------- | -------------------- |
|
||||||
| option | `string` | | 选项说明 |
|
| option | `string` | | 选项说明 |
|
||||||
| onText | `string / IconSchema` | | 开启时开关显示的内容 |
|
| onText | `string / IconSchema` | | 开启时开关显示的内容 |
|
||||||
| offText | `string / IconSchema` | | 关闭时开关显示的内容 |
|
| offText | `string / IconSchema` | | 关闭时开关显示的内容 |
|
||||||
| trueValue | `boolean / string / number` | `true` | 标识真值 |
|
| trueValue | `boolean / string / number` | `true` | 标识真值 |
|
||||||
| falseValue | `boolean / string / number` | `false` | 标识假值 |
|
| falseValue | `boolean / string / number` | `false` | 标识假值 |
|
||||||
|
|
||||||
IconSchema 配置
|
IconSchema 配置
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ---------- | -------- | --------- | ------------ |
|
| ---------- | -------- | --------- | ------------ |
|
||||||
| type | `string` | | `icon` |
|
| type | `string` | | `icon` |
|
||||||
| icon | `string` | | icon的类型 |
|
| icon | `string` | | icon 的类型 |
|
||||||
|
@ -12,6 +12,19 @@
|
|||||||
background: var(--DatePicker-bg);
|
background: var(--DatePicker-bg);
|
||||||
border-radius: var(--DatePicker-borderRadius);
|
border-radius: var(--DatePicker-borderRadius);
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
&:focus {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include input-border();
|
@include input-border();
|
||||||
|
|
||||||
&:not(.is-disabled) {
|
&:not(.is-disabled) {
|
||||||
|
@ -17,6 +17,7 @@ import Calendar from './calendar/Calendar';
|
|||||||
import {localeable, LocaleProps, TranslateFn} from '../locale';
|
import {localeable, LocaleProps, TranslateFn} from '../locale';
|
||||||
import {isMobile, ucFirst} from '../utils/helper';
|
import {isMobile, ucFirst} from '../utils/helper';
|
||||||
import CalendarMobile from './CalendarMobile';
|
import CalendarMobile from './CalendarMobile';
|
||||||
|
import Input from './Input';
|
||||||
|
|
||||||
const availableShortcuts: {[propName: string]: any} = {
|
const availableShortcuts: {[propName: string]: any} = {
|
||||||
now: {
|
now: {
|
||||||
@ -302,6 +303,7 @@ export interface DatePickerState {
|
|||||||
isOpened: boolean;
|
isOpened: boolean;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
value: moment.Moment | undefined;
|
value: moment.Moment | undefined;
|
||||||
|
inputValue: string | undefined; // 手动输入的值
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeValue(value: any, format?: string) {
|
function normalizeValue(value: any, format?: string) {
|
||||||
@ -329,11 +331,15 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
state: DatePickerState = {
|
state: DatePickerState = {
|
||||||
isOpened: false,
|
isOpened: false,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
value: normalizeValue(this.props.value, this.props.format)
|
value: normalizeValue(this.props.value, this.props.format),
|
||||||
|
inputValue:
|
||||||
|
normalizeValue(this.props.value, this.props.format)?.format(
|
||||||
|
this.props.inputFormat
|
||||||
|
) || ''
|
||||||
};
|
};
|
||||||
constructor(props: DateProps) {
|
constructor(props: DateProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.inputRef = React.createRef();
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
this.selectRannge = this.selectRannge.bind(this);
|
this.selectRannge = this.selectRannge.bind(this);
|
||||||
this.checkIsValidDate = this.checkIsValidDate.bind(this);
|
this.checkIsValidDate = this.checkIsValidDate.bind(this);
|
||||||
@ -348,10 +354,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
this.getTarget = this.getTarget.bind(this);
|
this.getTarget = this.getTarget.bind(this);
|
||||||
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
||||||
this.renderShortCuts = this.renderShortCuts.bind(this);
|
this.renderShortCuts = this.renderShortCuts.bind(this);
|
||||||
|
this.inputChange = this.inputChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
dom: HTMLDivElement;
|
dom: HTMLDivElement;
|
||||||
|
|
||||||
|
inputRef: React.RefObject<HTMLInputElement>;
|
||||||
|
|
||||||
componentDidUpdate(prevProps: DateProps) {
|
componentDidUpdate(prevProps: DateProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
|
|
||||||
@ -403,13 +412,17 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open(fn?: () => void) {
|
open(fn?: () => void) {
|
||||||
this.props.disabled ||
|
if (this.props.disabled) {
|
||||||
this.setState(
|
return;
|
||||||
{
|
}
|
||||||
isOpened: true
|
this.setState(
|
||||||
},
|
{
|
||||||
fn
|
isOpened: true
|
||||||
);
|
},
|
||||||
|
fn
|
||||||
|
);
|
||||||
|
const input = this.inputRef.current;
|
||||||
|
input && input.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
@ -423,6 +436,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
onChange('');
|
onChange('');
|
||||||
|
this.setState({inputValue: ''});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(value: moment.Moment) {
|
handleChange(value: moment.Moment) {
|
||||||
@ -432,6 +446,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
minDate,
|
minDate,
|
||||||
maxDate,
|
maxDate,
|
||||||
dateFormat,
|
dateFormat,
|
||||||
|
inputFormat,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
closeOnSelect,
|
closeOnSelect,
|
||||||
utc,
|
utc,
|
||||||
@ -453,6 +468,31 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
if (closeOnSelect && dateFormat && !timeFormat) {
|
if (closeOnSelect && dateFormat && !timeFormat) {
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
inputValue: utc
|
||||||
|
? moment.utc(value).format(inputFormat)
|
||||||
|
: value.format(inputFormat)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动输入日期
|
||||||
|
inputChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const {onChange, inputFormat, format, utc} = this.props;
|
||||||
|
const value = e.currentTarget.value;
|
||||||
|
this.setState({inputValue: value});
|
||||||
|
if (value === '') {
|
||||||
|
onChange('');
|
||||||
|
} else {
|
||||||
|
const newDate = moment(value, inputFormat);
|
||||||
|
const dateValue = utc
|
||||||
|
? moment.utc(newDate).format(format)
|
||||||
|
: newDate.format(format);
|
||||||
|
// 小于 0 的日期丢弃
|
||||||
|
if (!dateValue.startsWith('-')) {
|
||||||
|
onChange(dateValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectRannge(item: any) {
|
selectRannge(item: any) {
|
||||||
@ -693,15 +733,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
ref={this.domRef}
|
ref={this.domRef}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
>
|
>
|
||||||
{date ? (
|
<Input
|
||||||
<span className={cx(`DatePicker-value`)}>
|
onChange={this.inputChange}
|
||||||
{date.format(inputFormat)}
|
ref={this.inputRef}
|
||||||
</span>
|
placeholder={__(placeholder)}
|
||||||
) : (
|
autoComplete="off"
|
||||||
<span className={cx(`DatePicker-placeholder`)}>
|
value={this.state.inputValue}
|
||||||
{__(placeholder)}
|
/>
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{clearable && !disabled && normalizeValue(value, format) ? (
|
{clearable && !disabled && normalizeValue(value, format) ? (
|
||||||
<a className={cx(`DatePicker-clear`)} onClick={this.clearValue}>
|
<a className={cx(`DatePicker-clear`)} onClick={this.clearValue}>
|
||||||
|
@ -161,18 +161,19 @@ class BaseDatePicker extends React.Component<
|
|||||||
updatedState = this.getStateFromProps(props);
|
updatedState = this.getStateFromProps(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedState.open === undefined) {
|
// open 是外部控制了
|
||||||
if (typeof props.open !== 'undefined') {
|
// if (updatedState.open === undefined) {
|
||||||
updatedState.open = props.open;
|
// if (typeof props.open !== 'undefined') {
|
||||||
} else if (
|
// updatedState.open = props.open;
|
||||||
prevProps.closeOnSelect &&
|
// } else if (
|
||||||
this.state.currentView !== viewModes.TIME
|
// prevProps.closeOnSelect &&
|
||||||
) {
|
// this.state.currentView !== viewModes.TIME
|
||||||
updatedState.open = false;
|
// ) {
|
||||||
} else {
|
// updatedState.open = false;
|
||||||
updatedState.open = this.state.open;
|
// } else {
|
||||||
}
|
// updatedState.open = this.state.open;
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (props.viewMode !== prevProps.viewMode) {
|
if (props.viewMode !== prevProps.viewMode) {
|
||||||
updatedState.currentView = props.viewMode;
|
updatedState.currentView = props.viewMode;
|
||||||
@ -236,6 +237,10 @@ class BaseDatePicker extends React.Component<
|
|||||||
updatedState.viewDate = moment(props.viewDate);
|
updatedState.viewDate = moment(props.viewDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updatedState).length) {
|
||||||
|
this.setState(updatedState);
|
||||||
|
}
|
||||||
|
|
||||||
this.checkTZ(props);
|
this.checkTZ(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user