feat: inputNumber配置单位支持label/value这种配置,显示和给后端的单位区分开 (#8435)

* feat: inputNumber配置单位支持label/value这种配置,显示和给后端的单位区分开

* 不在schema内写变量,属性通过props传入

---------

Co-authored-by: yinchunyu <yinchunyu@baidu.com>
This commit is contained in:
yinchunyu 2023-10-24 16:16:12 +08:00 committed by GitHub
parent cf3eb0f09c
commit b994d72dcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 416 additions and 43 deletions

View File

@ -0,0 +1,46 @@
.ae-KeyValMapControl-wrapper {
.ae-KeyValMapControlItem-Main {
width: 100%;
display: flex;
justify-content: space-between;
}
.ae-KeyValMapControlItem-flex {
width: 100%;
}
.ae-KeyValMapControlItem-input {
flex: 1;
margin-right: 10px;
}
.ae-ExtendMore {
position: relative;
.cxd-Container-body {
margin-top: 20px;
}
}
.ae-KeyValMapControlItem-closeBtn {
position: absolute;
top: 0;
right: 0;
font-size: 18px;
color: #151b26;
padding-right: 0.625rem;
z-index: 1;
}
.ae-KeyValMapControlItem-EditLabel {
margin-top: 10px;
}
.ae-KeyValMapControl-footer > * {
width: calc(50% - 0.375rem);
}
.ae-KeyValMapControl-footer > *:first-child {
margin-right: 0.75rem;
}
}

View File

@ -38,6 +38,7 @@
@import './control/tree_option_control';
@import './control/_inpupt-file';
@import './control/_nav-control';
@import './control/_key-value-map-control';
@import './control/_status';
@import './control/_icon-button-group-control';
@import './control/_flex-setting-control';

View File

@ -7,6 +7,7 @@ export * from './plugin';
import './renderer/OptionControl';
import './renderer/NavSourceControl';
import './renderer/KeyValueMapControl';
import './renderer/NavBadgeControl';
import './renderer/NavDefaultActive';
import './renderer/MapSourceControl';

View File

@ -223,49 +223,7 @@ export class NumberControlPlugin extends BasePlugin {
},
getSchemaTpl('prefix'),
getSchemaTpl('suffix'),
getSchemaTpl('combo-container', {
type: 'combo',
label: '单位选项',
mode: 'normal',
name: 'unitOptions',
items: [
{
placeholder: 'label',
type: i18nEnabled ? 'input-text-i18n' : 'input-text',
name: 'label'
},
{
placeholder: 'value',
type: i18nEnabled ? 'input-text-i18n' : 'input-text',
name: 'value'
}
],
draggable: false,
multiple: true,
pipeIn: (value: any) => {
if (!isObject(value)) {
if (Array.isArray(value)) {
return value.every(item => typeof item === 'string')
? value.map((item: any) => ({
label: item,
value: item
}))
: value;
}
return [];
}
return value.map((item: any) => ({
label: item.value,
value: item.value
}));
},
pipeOut: (value: any[]) => {
if (!value.length) {
return undefined;
}
return value;
}
}),
getSchemaTpl('keyValueMapControl'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),

View File

@ -0,0 +1,357 @@
import React from 'react';
import {FormItem, Button, render as amisRender} from 'amis';
import {autobind, getI18nEnabled} from 'amis-editor-core';
import uniqBy from 'lodash/uniqBy';
import type {FormControlProps} from 'amis-core';
interface IOption {
label: string;
value: string;
editing?: boolean;
}
export interface KeyValueControlProps extends FormControlProps {}
export interface KeyValueControlState {
unitOptions: IOption[];
}
@FormItem({
type: 'ae-keyValueMapControl'
})
export class KeyValueMapControl extends React.Component<
KeyValueControlProps,
KeyValueControlState
> {
constructor(props: KeyValueControlProps) {
super(props);
this.state = {
unitOptions: this.transformOptions(props)
};
}
transformOptions(props: KeyValueControlProps) {
const {value} = props;
if (Array.isArray(value)) {
return value.map(item =>
typeof item === 'string'
? {
label: item,
value: item
}
: item
);
} else {
return [];
}
}
/**
*
*/
componentWillReceiveProps(nextProps: KeyValueControlProps) {
const unitOptions = nextProps.value ? this.transformOptions(nextProps) : [];
if (
JSON.stringify(
this.state.unitOptions.map(item => ({
...item,
editing: undefined
}))
) !== JSON.stringify(unitOptions)
) {
this.setState({
unitOptions
});
}
}
/**
*
*/
@autobind
handleAdd() {
const {unitOptions} = this.state;
unitOptions.push({
label: '',
value: '',
editing: false
});
this.setState({unitOptions}, () => {
this.onChange();
});
}
/**
*
*/
@autobind
handleBatchAdd(values: {batchOption: string}[]) {
const unitOptions = this.state.unitOptions.concat();
const addedOptions: Array<{label: string; value: string}> =
values[0].batchOption.split('\n').map(option => {
const item = option.trim();
if (~item.indexOf(' ')) {
let [label, value] = item.split(' ');
return {label: label.trim(), value: value.trim()};
}
return {label: item, value: item};
});
const newOptionsUniqByLabel = uniqBy(
[...unitOptions, ...addedOptions],
'label'
);
const newOptions = uniqBy(newOptionsUniqByLabel, 'value');
this.setState({unitOptions: newOptions}, () => this.onChange());
}
/**
*
*/
@autobind
handleEditLabel(index: number, value: string) {
const unitOptions = this.state.unitOptions.concat();
unitOptions.splice(index, 1, {...unitOptions[index], label: value});
this.setState({unitOptions}, () => this.onChange());
}
/**
*
*/
@autobind
handleValueChange(index: number, value: string) {
const unitOptions = this.state.unitOptions.concat();
unitOptions.splice(index, 1, {...unitOptions[index], value: value});
this.setState({unitOptions}, () => this.onChange());
}
/**
*
*/
toggleEdit(index: number) {
const {unitOptions} = this.state;
unitOptions[index].editing = !unitOptions[index].editing;
this.setState({unitOptions});
}
/**
*
*/
handleDelete(index: number) {
const unitOptions = this.state.unitOptions.concat();
unitOptions.splice(index, 1);
this.setState({unitOptions}, () => this.onChange());
}
/**
* unitOptions字段的统一出口
*/
onChange() {
const {onBulkChange} = this.props;
const {unitOptions} = this.state;
const options = unitOptions.map(item => ({
label: item.label,
value: item.value
}));
onBulkChange && onBulkChange({unitOptions: options});
return;
}
renderOption(item: IOption, index: number) {
const {label, value, editing} = item;
const {render} = this.props;
const i18nEnabled = getI18nEnabled();
const editDom = editing ? (
<div className="ae-KeyValMapControlItem-extendMore">
{render('option', {
type: 'container',
className: 'ae-ExtendMore right mb-2',
body: [
{
type: 'button',
className: 'ae-KeyValMapControlItem-closeBtn',
label: '×',
level: 'link',
onClick: () => this.toggleEdit(index)
},
{
children: ({render}: any) =>
render(
'label',
{
type: i18nEnabled ? 'input-text-i18n' : 'input-text',
placeholder: '显示文本',
label: '文本',
mode: 'horizontal',
name: 'optionLabel',
labelClassName: 'ae-KeyValMapControlItem-EditLabel',
valueClassName: 'ae-KeyValMapControlItem-EditValue'
},
{
value: label,
onChange: (v: string) => this.handleEditLabel(index, v)
}
)
},
{
children: ({render}: any) =>
render(
'value',
{
type: i18nEnabled ? 'input-text-i18n' : 'input-text',
placeholder: '值内容',
label: '值',
mode: 'horizontal',
name: 'optionValue',
labelClassName: 'ae-KeyValMapControlItem-EditLabel',
valueClassName: 'ae-KeyValMapControlItem-EditValue'
},
{
value,
onChange: (v: string) => this.handleValueChange(index, v)
}
)
}
]
})}
</div>
) : null;
const operationBtn = [
{
type: 'button',
className: 'ae-KeyValMapControlItem-action',
label: '编辑',
onClick: () => this.toggleEdit(index)
},
{
type: 'button',
className: 'ae-KeyValMapControlItem-action',
label: '删除',
onClick: () => this.handleDelete(index)
}
];
return (
<div className="ae-KeyValMapControlItem">
<div className="ae-KeyValMapControlItem-Main">
{render('dropdown', {
type: 'flex',
className: 'ae-KeyValMapControlItem-flex',
items: [
{
children: ({render}: any) =>
render(
'value',
{
type: i18nEnabled ? 'input-text-i18n' : 'input-text',
className: 'ae-KeyValMapControlItem-input',
label: false,
name: 'optionLabel',
placeholder: '请输入文本/值',
clearable: false
},
{
value: label,
onChange: (value: string) => {
this.handleEditLabel(index, value);
}
}
)
},
{
type: 'dropdown-button',
className: 'ae-KeyValMapControlItem-dropdown',
btnClassName: 'px-2',
icon: 'fa fa-ellipsis-h',
hideCaret: true,
closeOnClick: true,
align: 'right',
menuClassName: 'ae-KeyValMapControlItem-ulmenu',
buttons: operationBtn
}
]
})}
</div>
{editDom}
</div>
);
}
buildBatchAddSchema() {
return {
type: 'action',
actionType: 'dialog',
label: '批量添加',
dialog: {
title: '批量添加选项',
headerClassName: 'font-bold',
closeOnEsc: true,
closeOnOutside: false,
showCloseButton: true,
onConfirm: this.handleBatchAdd,
body: [
{
type: 'alert',
level: 'warning',
body: [
{
type: 'tpl',
tpl: '每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
}
],
showIcon: true,
className: 'mb-2.5'
},
{
type: 'form',
wrapWithPanel: false,
mode: 'normal',
wrapperComponent: 'div',
resetAfterSubmit: true,
autoFocus: true,
preventEnterSubmit: true,
horizontal: {
left: 0,
right: 12
},
body: [
{
name: 'batchOption',
type: 'textarea',
label: '',
placeholder: '请输入选项内容',
trimContents: true,
minRows: 10,
maxRows: 50,
required: true
}
]
}
]
}
};
}
render() {
const {unitOptions} = this.state;
const {render} = this.props;
return (
<div className="ae-KeyValMapControl-wrapper">
{unitOptions.length && (
<div>
{unitOptions.map((item, index) => this.renderOption(item, index))}
</div>
)}
<div className="ae-KeyValMapControl-footer">
<Button level="enhance" onClick={this.handleAdd}>
</Button>
{render('inner', this.buildBatchAddSchema())}
</div>
</div>
);
}
}

View File

@ -739,3 +739,13 @@ setSchemaTpl('optionDeleteControl', (params: OptionControlParams) => {
}
});
});
/**
* key value映射类组件
*/
setSchemaTpl('keyValueMapControl', {
type: 'ae-keyValueMapControl',
label: '单位选项',
name: 'unitOptions',
mode: 'normal'
});