feat: 新增 input-kvs 组件支持复杂对象嵌套场景

This commit is contained in:
wuduoyi 2022-07-07 16:22:20 +08:00 committed by RUNZE LU
parent ad41eb807c
commit b7f6b87209
10 changed files with 691 additions and 3 deletions

View File

@ -15,6 +15,7 @@ order: 9
```schema: scope="body"
{
"type": "form",
"debug": true,
"api": "/api/mock2/form/saveForm",
"body": [
{
@ -44,6 +45,81 @@ order: 9
}
```
## 结果是数组模式
默认是拼接成字符串,如果希望结果是数组,可以设置 `"joinValues": false`
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"name": "checkboxes",
"type": "checkboxes",
"label": "复选框",
"joinValues": false,
"options": [
{
"label": "OptionA",
"value": "a"
},
{
"label": "OptionB",
"value": "b"
},
{
"label": "OptionC",
"value": "c"
},
{
"label": "OptionD",
"value": "d"
}
]
}
]
}
```
如果只想提取 value需要加上 `"extractValue": true`
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"name": "checkboxes",
"type": "checkboxes",
"label": "复选框",
"joinValues": false,
"extractValue": true,
"options": [
{
"label": "OptionA",
"value": "a"
},
{
"label": "OptionB",
"value": "b"
},
{
"label": "OptionC",
"value": "c"
},
{
"label": "OptionD",
"value": "d"
}
]
}
]
}
```
## 显示全选
通过 `checkAll` 属性配置全选

View File

@ -0,0 +1,320 @@
---
title: InputKVS 键值对象
description:
type: 0
group: null
menuName: InputKVS
icon:
order: 15
---
> 2.1.0 及以上版本
这个组件的功能和 `input-kv` 类似,`input-kv` 的 value 值只支持一个对象,`input-kvs` 的最大不同就是 value 支持对象和数组,可以用来支持深层结构编辑
## 基本用法
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "field",
"addButtonText": "新增字段",
"keyItem": {
"label": "字段名"
},
"valueItems": [
{
"type": "switch",
"name": "primary",
"label": "是否是主键"
},
{
"type": "select",
"name": "type",
"label": "字段类型",
"options": [
"text",
"int",
"float"
]
}
]
}
]
}
```
其中 `keyItem` 可以用来修改 key 值控件,比如可以改成下拉框
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "field",
"addButtonText": "新增字段",
"keyItem": {
"label": "字段名",
"type": "select",
"options": [
"id",
"title",
"content"
]
},
"valueItems": [
{
"type": "switch",
"name": "primary",
"label": "是否是主键"
},
{
"type": "select",
"name": "type",
"label": "字段类型",
"options": [
"text",
"int",
"float"
]
}
]
}
]
}
```
`valueItems` 是用来控制值的控件,这里的配置和 combo 的 items 一样,唯一限制是不允许有 `"name": "_key"` 值的情况,因为这个值被当成对象 key 了。
## 水平模式
通过 `"mode": "horizontal"` 设置,需要分别在 `keyItem``valueItems` 里设置
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "field",
"addButtonText": "新增字段",
"keyItem": {
"label": "字段名",
"mode": "horizontal"
},
"valueItems": [
{
"type": "switch",
"name": "primary",
"mode": "horizontal",
"label": "是否是主键"
},
{
"type": "select",
"name": "type",
"label": "字段类型",
"mode": "horizontal",
"options": [
"text",
"int",
"float"
]
}
]
}
]
}
```
## 嵌套的场景
`valueItems` 可以进一步嵌套,比如里面又嵌一个 `input-kvs` 实现深层结构编辑
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "dataModel",
"addButtonText": "新增表",
"keyItem": {
"label": "表名",
"mode": "horizontal",
"type": "select",
"options": [
"table1",
"table2",
"table3"
]
},
"valueItems": [
{
"type": "input-kvs",
"addButtonText": "新增字段",
"name": "column",
"keyItem": {
"label": "字段名",
"mode": "horizontal",
"type": "select",
"options": [
"id",
"title",
"content"
]
},
"valueItems": [
{
"type": "switch",
"name": "primary",
"mode": "horizontal",
"label": "是否是主键"
},
{
"type": "select",
"name": "type",
"label": "字段类型",
"mode": "horizontal",
"options": [
"text",
"int",
"float"
]
}
]
}
]
}
]
}
```
前面的嵌套会多一个层级,如果想去掉这个层级 `column`,可以将 `"name": "column"` 改成 `"name": "_value"`,这时值就会直接放入
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "dataModel",
"addButtonText": "新增表",
"keyItem": {
"label": "表名",
"mode": "horizontal",
"type": "select",
"options": [
"table1",
"table2",
"table3"
]
},
"valueItems": [
{
"type": "input-kvs",
"addButtonText": "新增字段",
"name": "_value",
"keyItem": {
"label": "字段名",
"mode": "horizontal",
"type": "select",
"options": [
"id",
"title",
"content"
]
},
"valueItems": [
{
"type": "switch",
"name": "primary",
"mode": "horizontal",
"label": "是否是主键"
},
{
"type": "select",
"name": "type",
"label": "字段类型",
"mode": "horizontal",
"options": [
"text",
"int",
"float"
]
}
]
}
]
}
]
}
```
除了前面的对象,值也可以是数组,需要配置一下 `valueIsArray`
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"debug": true,
"body": [
{
"type": "input-kvs",
"name": "dataModel",
"addButtonText": "新增表",
"valueIsArray": true,
"keyItem": {
"label": "表名",
"mode": "horizontal",
"type": "select",
"options": [
"table1",
"table2",
"table3"
]
},
"valueItems": [
{
type: 'checkboxes',
name: '_value',
joinValues: false,
extractValue: true,
options: [
{
label: '查询',
value: 'select'
},
{
label: '写入',
value: 'insert'
},
{
label: '更新',
value: 'update'
},
{
label: '删除',
value: 'delete'
}
]
}
]
}
]
}
```

View File

@ -78,7 +78,7 @@ _特殊字符变量名_
### 公式
除了支持简单表达式外,还集成了很多公式(函数)如:
除了支持简单表达式外,还集成了很多公式(函数)如 `${ AVG(1, 2, 3, 4)}`
```schema
{
@ -95,10 +95,10 @@ _特殊字符变量名_
}
```
!!!include(amis-formula/lib/doc.md)!!!
## 函数调用示例
函数支持嵌套,参数支持常量及变量
```schema
{
"type": "page",
@ -106,6 +106,9 @@ _特殊字符变量名_
{
"type": "form",
"wrapWithPanel": false,
"data": {
"val": 3.5
},
"body": [
{
"type": "static",
@ -122,6 +125,11 @@ _特殊字符变量名_
"label": "ROUND(3.5)",
"tpl": "${ROUND(3.5)}"
},
{
"type": "static",
"label": "ROUND(val)",
"tpl": "${ROUND(val)}"
},
{
"type": "static",
"label": "AVG(4, 6, 10, 10, 10)",
@ -147,3 +155,7 @@ _特殊字符变量名_
]
}
```
下面是目前所支持函数的使用手册
!!!include(amis-formula/lib/doc.md)!!!

View File

@ -376,6 +376,13 @@ export const components = [
import('../../docs/zh-CN/components/form/input-kv.md').then(wrapDoc)
)
},
{
label: 'InputKVS 键值对象',
path: '/zh-CN/components/form/input-kvs',
component: React.lazy(() =>
import('../../docs/zh-CN/components/form/input-kvs.md').then(wrapDoc)
)
},
{
label: 'InputFormula 公式编辑器',
path: '/zh-CN/components/form/input-formula',

View File

@ -30,6 +30,7 @@ import CustomFormSchema from './Form/Custom';
import FormLayoutTestSchema from './Form/layoutTest';
import Definitions from './Form/Definitions';
import AnchorNav from './Form/AnchorNav';
import InputKVSSchema from './Form/InputKVS';
import TableCrudSchema from './CRUD/Table';
import TableAutoFillSchema from './CRUD/TableAutoFill';
@ -294,6 +295,12 @@ export const examples = [
label: '锚点导航',
path: '/examples/form/anchor-nav',
component: makeSchemaRenderer(AnchorNav)
},
{
label: '复杂嵌套数据',
path: '/examples/form/input-kvs',
component: makeSchemaRenderer(InputKVSSchema)
}
// {

View File

@ -925,6 +925,27 @@ export default {
label: 'kv 输入',
name: 'kv'
},
{
type: 'input-kvs',
name: 'kvs',
label: 'kvs',
keyItem: {
label: '字段名'
},
valueItems: [
{
type: 'switch',
name: 'primary',
label: '是否是主键'
},
{
type: 'select',
name: 'type',
label: '字段类型',
options: ['text', 'int', 'number']
}
]
},
{
type: 'divider'
},

View File

@ -0,0 +1,127 @@
export default {
type: 'page',
body: {
type: 'form',
title: 'input-kvs 实现复杂嵌套数据结构',
mode: 'horizontal',
debug: true,
submitText: '',
submitOnChange: false,
body: [
{
type: 'input-kvs',
name: 'dataModel',
addButtonText: '新增数据源权限',
keyItem: {
label: '数据源名',
type: 'select',
options: ['local-mysql', 'remote-postgres', 'cloud-oracle']
},
valueItems: [
{
label: '数据源级别权限',
type: 'checkboxes',
name: 'permissions',
joinValues: false,
extractValue: true,
options: [
{
label: '查询',
value: 'select'
},
{
label: '写入',
value: 'insert'
},
{
label: '更新',
value: 'update'
},
{
label: '删除',
value: 'delete'
}
]
},
{
type: 'input-kvs',
label: '表权限',
addButtonText: '新增表权限',
name: 'dataModel',
keyItem: {
label: '表名',
mode: 'horizontal',
type: 'select',
options: ['table1', 'table2', 'table3']
},
valueItems: [
{
type: 'switch',
name: 'canInsert',
mode: 'horizontal',
label: '是否可写入'
},
{
type: 'switch',
name: 'canDelete',
mode: 'horizontal',
label: '是否可删除'
},
{
type: 'input-kvs',
label: '字段级别权限',
addButtonText: '新增字段权限',
name: 'field',
keyItem: {
label: '字段名',
mode: 'horizontal',
type: 'select',
options: ['id', 'title', 'content']
},
valueIsArray: true,
valueItems: [
{
type: 'checkboxes',
name: '_value',
joinValues: false,
extractValue: true,
options: [
{
label: '查询',
value: 'select'
},
{
label: '写入',
value: 'insert'
},
{
label: '更新',
value: 'update'
},
{
label: '删除',
value: 'delete'
}
]
}
]
},
{
name: 'fieldSelectExp',
label: '字段表达式权限',
type: 'input-array',
value: ['SUM(fieldKey)'],
addButtonText: '新增表达式权限',
inline: true,
items: {
size: 'md',
type: 'input-text'
}
}
]
}
]
}
]
}
};

View File

@ -1456,6 +1456,7 @@ export class Evaluator {
*
*
* @example TIMESTAMP(date[, format = "X"])
* @example TIMESTAMP(date, 'x')
* @namespace
* @param {date} date
* @param {string} format 'x' 'X'
@ -1494,6 +1495,7 @@ export class Evaluator {
*
*
* @example DATETOSTR(date[, format="YYYY-MM-DD HH:mm:ss"])
* @example DATETOSTR(date, 'YYYY-MM-DD')
* @namespace
* @param {date} date
* @param {string} format "YYYY-MM-DD HH:mm:ss"

View File

@ -620,6 +620,23 @@ export default class ComboControl extends React.Component<ComboProps> {
if (!hasDuplicateKey) {
this.props.onChange(value, submitOnChange, true);
}
} else if (type === 'input-kvs') {
// input-kvs 为了避免冲突 key 改成了 _key
let hasDuplicateKey = false;
const keys: {[key: string]: boolean} = {};
for (const item of value) {
if ('_key' in item) {
if (keys[item._key]) {
hasDuplicateKey = true;
} else {
keys[item._key] = true;
}
}
}
// 有重复值就不触发修改,因为 KV 模式下无法支持重复值
if (!hasDuplicateKey) {
this.props.onChange(value, submitOnChange, true);
}
} else {
this.props.onChange(value, submitOnChange, true);
}
@ -1614,3 +1631,10 @@ export class ComboControlRenderer extends ComboControl {
extendsData: false
})
export class KVControlRenderer extends ComboControl {}
@FormItem({
type: 'input-kvs',
storeType: ComboStore.name,
extendsData: false
})
export class KVSControlRenderer extends ComboControl {}

View File

@ -1,6 +1,7 @@
/**
* Schema input-kv
*/
import isEqual from 'lodash/isEqual';
import {Schema} from 'amis-core';
import {addSchemaFilter} from 'amis-core';
import {isObject} from 'amis-core';
@ -76,3 +77,94 @@ addSchemaFilter(function (schema: Schema, renderer, props?: any) {
return schema;
});
/**
* input-kv value
* valuePlaceholder
* value
*/
addSchemaFilter(function (schema: Schema, renderer, props?: any) {
if (schema && schema.type === 'input-kvs') {
const keyItem = schema.keyItem || {};
const valueItems = schema.valueItems || [];
// value 直接放在 key 下的情况
let flatValue = false;
if (valueItems.length == 1) {
if (valueItems[0].name === '_value') {
flatValue = true;
}
}
const newSchema = {
draggable: true,
multiple: true,
multiLine: true,
...schema,
pipeIn: (data: any) => {
if (!isObject(data)) {
return [];
}
const arr: Array<any> = [];
Object.keys(data).forEach(key => {
let value = data[key];
if (!value) {
// value = {};
}
if (flatValue) {
arr.push({
_key: key || '',
_value: value
});
} else if (typeof value === 'object') {
arr.push({
...value,
_key: key || ''
});
}
});
return arr;
},
pipeOut: (value: any) => {
if (!Array.isArray(value)) {
return value;
}
const obj: any = {};
value.forEach((item: any) => {
let {_key, ...rest} = item;
_key = _key ?? '';
if (flatValue) {
if (schema.valueIsArray) {
obj[_key] = item['_value'] || [];
} else {
obj[_key] = item['_value'] || {};
}
// 数组的时候初始化会生成 [{}],还不确定是哪生成的,先修正为 []
if (isEqual(obj[_key], [{}])) {
obj[_key] = [];
}
} else {
if (schema.valueIsArray) {
obj[_key] = rest || [];
} else {
obj[_key] = rest || {};
}
}
});
return obj;
},
items: [
{
type: 'input-text',
unique: true,
name: '_key',
required: true,
validateOnChange: true,
...keyItem
},
...valueItems
]
};
return newSchema;
}
return schema;
});