feat: 表单label支持左右对齐方式 (#4311)

This commit is contained in:
RUNZE LU 2022-05-12 10:24:26 +08:00 committed by GitHub
parent e22e2f80ce
commit acc101b425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 45 deletions

View File

@ -1068,29 +1068,30 @@ Table 类型的表单项,要实现服务端校验,可以使用 `路径key`
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | -------------------------------------------------- | ------ | ---------------------------------------------------------- |
| type | `string` | | 指定表单项类型 |
| className | `string` | | 表单最外层类名 |
| inputClassName | `string` | | 表单控制器类名 |
| labelClassName | `string` | | label 的类名 |
| name | `string` | | 字段名,指定该表单项提交时的 key |
| value | `string` | | 表单默认值 |
| label | [模板](../../../docs/concepts/template) 或 `false` | | 表单项标签 |
| labelRemark | [Remark](../remark) | | 表单项标签描述 |
| description | [模板](../../../docs/concepts/template) | | 表单项描述 |
| placeholder | `string` | | 表单项描述 |
| inline | `boolean` | | 是否为 内联 模式 |
| submitOnChange | `boolean` | | 是否该表单项值发生变化时就提交当前表单。 |
| disabled | `boolean` | | 当前表单项是否是禁用状态 |
| disabledOn | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| visible | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| visibleOn | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| required | `boolean` | | 是否为必填。 |
| requiredOn | [表达式](../../../docs/concepts/expression) | | 过[表达式](../Types.md#表达式)来配置当前表单项是否为必填。 |
| validations | [表达式](../../../docs/concepts/expression) | | 表单项值格式验证,支持设置多个,多个规则用英文逗号隔开。 |
| validateApi | [表达式](../../../docs/types/api) | | 表单校验接口 |
| autoUpdate | Object | | 自动填充配置 |
| autoUpdate.api | [api](../../types/api) | | 自动填充数据接口地址 |
| autoUpdate.mapping | Object | | 自动填充字段映射关系 |
| autoUpdate.showToast | `boolean` | | 是否展示数据格式错误提示,默认为 false |
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | -------------------------------------------------- | --------- | ---------------------------------------------------------------- |
| type | `string` | | 指定表单项类型 |
| className | `string` | | 表单最外层类名 |
| inputClassName | `string` | | 表单控制器类名 |
| labelClassName | `string` | | label 的类名 |
| name | `string` | | 字段名,指定该表单项提交时的 key |
| value | `string` | | 表单默认值 |
| label | [模板](../../../docs/concepts/template) 或 `false` | | 表单项标签 |
| labelAlign | `"right" \| "left"` | `"right"` | 表单项标签对齐方式,默认右对齐,仅在 `mode`为`horizontal` 时生效 |
| labelRemark | [Remark](../remark) | | 表单项标签描述 |
| description | [模板](../../../docs/concepts/template) | | 表单项描述 |
| placeholder | `string` | | 表单项描述 |
| inline | `boolean` | | 是否为 内联 模式 |
| submitOnChange | `boolean` | | 是否该表单项值发生变化时就提交当前表单。 |
| disabled | `boolean` | | 当前表单项是否是禁用状态 |
| disabledOn | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| visible | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| visibleOn | [表达式](../../../docs/concepts/expression) | | 当前表单项是否禁用的条件 |
| required | `boolean` | | 是否为必填。 |
| requiredOn | [表达式](../../../docs/concepts/expression) | | 过[表达式](../Types.md#表达式)来配置当前表单项是否为必填。 |
| validations | [表达式](../../../docs/concepts/expression) | | 表单项值格式验证,支持设置多个,多个规则用英文逗号隔开。 |
| validateApi | [表达式](../../../docs/types/api) | | 表单校验接口 |
| autoUpdate | Object | | 自动填充配置 |
| autoUpdate.api | [api](../../types/api) | | 自动填充数据接口地址 |
| autoUpdate.mapping | Object | | 自动填充字段映射关系 |
| autoUpdate.showToast | `boolean` | | 是否展示数据格式错误提示,默认为 false |

View File

@ -170,6 +170,8 @@ order: 24
}
```
#### 两端对齐
有时表单内容需要两端对齐,可在 horizontal 中增加 justify 配置,注意只对内联控件生效
```schema: scope="body"
@ -215,6 +217,34 @@ order: 24
}
```
#### label 对齐模式
水平模式下 `labelAlign` 可以设置标签文本的对齐方式,支持`right`和`left`,默认为`right`。该属性的优先级:表单项 > 表单。
```schema: scope="body"
{
"type": "form",
"title": "label对齐模式",
"mode": "horizontal",
"labelAlign": "left",
"body": [
{
"type": "input-email",
"name": "email",
"label": "邮箱",
"labelAlign": "right",
"required": true
},
{
"type": "input-password",
"name": "password",
"label": "密码",
"required": true
}
]
}
```
### 内联模式
使用内联模式展现表单项
@ -1160,6 +1190,7 @@ Form 支持轮询初始化接口,步骤如下:
| name | `string` | | 设置一个名字后,方便其他组件与其通信 |
| mode | `string` | `normal` | 表单展示方式,可以是:`normal`、`horizontal` 或者 `inline` |
| horizontal | `Object` | `{"left":"col-sm-2", "right":"col-sm-10", "offset":"col-sm-offset-2"}` | 当 mode 为 `horizontal` 时有用,用来控制 label |
| labelAlign | `"right" \| "left"` | `"right"` | 表单项标签对齐方式,默认右对齐,仅在 `mode`为`horizontal` 时生效 |
| title | `string` | `"表单"` | Form 的标题 |
| submitText | `String` | `"提交"` | 默认的提交按钮名称,如果设置成空,则可以把默认按钮去掉。 |
| className | `string` | | 外层 Dom 的类名 |
@ -1205,16 +1236,16 @@ Form 支持轮询初始化接口,步骤如下:
## 事件表
| 事件名称 | 事件参数 | 说明 |
| --------------------- | ----------------------------- | -------------- |
| inited | 表单数据 | 初始化完成 |
| change | 表单数据 | 值变化 |
| formItemValidateSucc | 表单数据 | 表单项校验成功 |
| formItemValidateError | 表单数据 | 表单项校验失败 |
| validateSucc | 表单数据 | 表单校验成功 |
| validateError | 表单数据 | 表单校验成功 |
| submitSucc | 配置api时 `result: object` 接口返回内容; 否则为表单数据 | 提交成功 |
| submitFail | `error: object` 接口返回内容 | 提交失败 |
| 事件名称 | 事件参数 | 说明 |
| --------------------- | ----------------------------------------------------------- | -------------- |
| inited | 表单数据 | 初始化完成 |
| change | 表单数据 | 值变化 |
| formItemValidateSucc | 表单数据 | 表单项校验成功 |
| formItemValidateError | 表单数据 | 表单项校验失败 |
| validateSucc | 表单数据 | 表单校验成功 |
| validateError | 表单数据 | 表单校验成功 |
| submitSucc | 配置 api 时: `result: object` 接口返回内容; 否则为表单数据 | 提交成功 |
| submitFail | `error: object` 接口返回内容 | 提交失败 |
## 动作表

View File

@ -420,7 +420,7 @@
@include input-clear();
}
&-passwordReveal {
&-revealPassword {
cursor: pointer;
color: var(--text--muted-color);
}

View File

@ -119,6 +119,10 @@
> .#{$ns}Form-label {
text-align: var(--Form--horizontal-label-align);
white-space: var(--Form--horizontal-label-whiteSpace);
&--left {
text-align: left;
}
}
}

View File

@ -40,6 +40,8 @@ import debounce from 'lodash/debounce';
import {isEffectiveApi} from '../../utils/api';
import {dataMapping} from '../../utils/tpl-builtin';
export type LabelAlign = 'right' | 'left';
export type FormControlSchemaAlias = SchemaObject;
export interface FormBaseControl
@ -60,6 +62,11 @@ export interface FormBaseControl
*/
label?: string | false;
/**
*
*/
labelAlign?: LabelAlign;
/**
* label className
*/
@ -324,8 +331,10 @@ export interface FormItemProps extends RendererProps {
formInited: boolean;
formMode: 'normal' | 'horizontal' | 'inline' | 'row' | 'default';
formHorizontal: FormHorizontal;
formLabelAlign: LabelAlign;
defaultSize?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
size?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
labelAlign?: LabelAlign;
disabled?: boolean;
btnDisabled: boolean;
defaultValue: any;
@ -605,6 +614,7 @@ export class FormItemWrap extends React.Component<FormItemProps> {
const horizontal = props.horizontal || props.formHorizontal || {};
const left = getWidthRate(horizontal.left);
const right = getWidthRate(horizontal.right);
const labelAlign = props.labelAlign || props.formLabelAlign;
return (
<div
@ -630,7 +640,8 @@ export class FormItemWrap extends React.Component<FormItemProps> {
? horizontal.leftFixed
: 'normal'
}`]: horizontal.leftFixed,
[`Form-itemColumn--${left}`]: !horizontal.leftFixed
[`Form-itemColumn--${left}`]: !horizontal.leftFixed,
'Form-label--left': labelAlign === 'left'
},
labelClassName
)}

View File

@ -54,9 +54,9 @@ import {
SchemaReload
} from '../../Schema';
import {ActionSchema} from '../Action';
import {ButtonGroupControlSchema} from './ButtonGroupSelect';
import {DialogSchemaBase} from '../Dialog';
import Alert from '../../components/Alert2';
import type {LabelAlign} from './Item';
export interface FormSchemaHorizontal {
left?: number;
@ -302,6 +302,11 @@ export interface FormSchema extends BaseSchema {
*
*/
preventEnterSubmit?: boolean;
/**
* label的对齐方式
*/
labelAlign?: LabelAlign;
}
export type FormGroup = FormSchema & {
@ -368,7 +373,8 @@ export default class Form extends React.Component<FormProps, object> {
},
wrapperComponent: '',
finishedField: 'finished',
initFinishedField: 'finished'
initFinishedField: 'finished',
labelAlign: 'right'
};
static propsList: Array<string> = [
'title',
@ -875,7 +881,10 @@ export default class Form extends React.Component<FormProps, object> {
if (!isAlive(store)) {
return;
}
const dispatcher = await dispatchEvent('change', createObject(data, store.data));
const dispatcher = await dispatchEvent(
'change',
createObject(data, store.data)
);
if (!dispatcher?.prevented) {
onChange &&
onChange(
@ -980,7 +989,10 @@ export default class Form extends React.Component<FormProps, object> {
if (Array.isArray(action.required) && action.required.length) {
return store.validateFields(action.required).then(async result => {
if (!result) {
const dispatcher = await dispatchEvent('validateError', this.props.data);
const dispatcher = await dispatchEvent(
'validateError',
this.props.data
);
if (!dispatcher?.prevented) {
env.notify('error', __('Form.validateFailed'));
}
@ -1059,7 +1071,7 @@ export default class Form extends React.Component<FormProps, object> {
(ret: any) => ret && ret[finishedField || 'finished'],
cancel => (this.asyncCancel = cancel),
checkInterval
)
);
return {
cbResult,
dispatcher
@ -1456,7 +1468,8 @@ export default class Form extends React.Component<FormProps, object> {
resolveDefinitions,
lazyChange,
formLazyChange,
dispatchEvent
dispatchEvent,
labelAlign
} = props;
const subProps = {
@ -1469,6 +1482,7 @@ export default class Form extends React.Component<FormProps, object> {
formSubmited: form.submited,
formMode: mode,
formHorizontal: horizontal,
formLabelAlign: labelAlign !== 'left' ? 'right' : labelAlign,
controlWidth,
disabled: disabled || (control as Schema).disabled || form.loading,
btnDisabled: disabled || form.loading || form.validating,