From b7f6b87209484dba9c0146523b8091679679577b Mon Sep 17 00:00:00 2001 From: wuduoyi Date: Thu, 7 Jul 2022 16:22:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20input-kvs=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=94=AF=E6=8C=81=E5=A4=8D=E6=9D=82=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=B5=8C=E5=A5=97=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh-CN/components/form/checkboxes.md | 76 +++++ docs/zh-CN/components/form/input-kvs.md | 320 +++++++++++++++++++++ docs/zh-CN/concepts/expression.md | 18 +- examples/components/Components.tsx | 7 + examples/components/Example.jsx | 7 + examples/components/Form/Full.jsx | 21 ++ examples/components/Form/InputKVS.jsx | 127 ++++++++ packages/amis-formula/src/evalutor.ts | 2 + packages/amis/src/renderers/Form/Combo.tsx | 24 ++ packages/amis/src/schemaExtend.ts | 92 ++++++ 10 files changed, 691 insertions(+), 3 deletions(-) create mode 100644 docs/zh-CN/components/form/input-kvs.md create mode 100644 examples/components/Form/InputKVS.jsx diff --git a/docs/zh-CN/components/form/checkboxes.md b/docs/zh-CN/components/form/checkboxes.md index 770790959..c54a6039b 100755 --- a/docs/zh-CN/components/form/checkboxes.md +++ b/docs/zh-CN/components/form/checkboxes.md @@ -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` 属性配置全选 diff --git a/docs/zh-CN/components/form/input-kvs.md b/docs/zh-CN/components/form/input-kvs.md new file mode 100644 index 000000000..4646a13f8 --- /dev/null +++ b/docs/zh-CN/components/form/input-kvs.md @@ -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' + } + ] + } + + ] + } + ] +} +``` diff --git a/docs/zh-CN/concepts/expression.md b/docs/zh-CN/concepts/expression.md index 9cdef893d..1639f0736 100755 --- a/docs/zh-CN/concepts/expression.md +++ b/docs/zh-CN/concepts/expression.md @@ -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)!!! diff --git a/examples/components/Components.tsx b/examples/components/Components.tsx index 53b0947dd..41b615cb7 100644 --- a/examples/components/Components.tsx +++ b/examples/components/Components.tsx @@ -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', diff --git a/examples/components/Example.jsx b/examples/components/Example.jsx index 44f03389c..9f94a8bef 100644 --- a/examples/components/Example.jsx +++ b/examples/components/Example.jsx @@ -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) } // { diff --git a/examples/components/Form/Full.jsx b/examples/components/Form/Full.jsx index e05b46688..a8463d558 100644 --- a/examples/components/Form/Full.jsx +++ b/examples/components/Form/Full.jsx @@ -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' }, diff --git a/examples/components/Form/InputKVS.jsx b/examples/components/Form/InputKVS.jsx new file mode 100644 index 000000000..f3545660e --- /dev/null +++ b/examples/components/Form/InputKVS.jsx @@ -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' + } + } + ] + } + ] + } + ] + } +}; diff --git a/packages/amis-formula/src/evalutor.ts b/packages/amis-formula/src/evalutor.ts index 06b600cbd..f2032945c 100644 --- a/packages/amis-formula/src/evalutor.ts +++ b/packages/amis-formula/src/evalutor.ts @@ -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" diff --git a/packages/amis/src/renderers/Form/Combo.tsx b/packages/amis/src/renderers/Form/Combo.tsx index ba1cffcdb..7a9f00364 100644 --- a/packages/amis/src/renderers/Form/Combo.tsx +++ b/packages/amis/src/renderers/Form/Combo.tsx @@ -620,6 +620,23 @@ export default class ComboControl extends React.Component { 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 {} diff --git a/packages/amis/src/schemaExtend.ts b/packages/amis/src/schemaExtend.ts index df99dfd03..56d87df4d 100644 --- a/packages/amis/src/schemaExtend.ts +++ b/packages/amis/src/schemaExtend.ts @@ -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 = []; + 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; +});