diff --git a/packages/amis-editor/src/component/BaseControl.ts b/packages/amis-editor/src/component/BaseControl.ts index a3e28ec640a..869cb7a9681 100644 --- a/packages/amis-editor/src/component/BaseControl.ts +++ b/packages/amis-editor/src/component/BaseControl.ts @@ -402,23 +402,11 @@ export function remarkTpl(config: { : config.label, bulk: false, name: config.name, - pipeIn: (value: any) => !!value, - pipeOut: (value: any) => { - // 更新内容 - if (isObject(value)) { - return value; - } - // 关到开 - if (value) { - return { - icon: 'fa fa-question-circle', - trigger: ['hover'], - className: 'Remark--warning', - placement: 'top' - }; - } - // 开到关 - return undefined; + defaultData: { + icon: 'fa fa-question-circle', + trigger: ['hover'], + className: 'Remark--warning', + placement: 'top' }, form: { size: 'md', diff --git a/packages/amis-editor/src/plugin/Carousel.tsx b/packages/amis-editor/src/plugin/Carousel.tsx index cd516bedbb9..7803bb8d7b9 100644 --- a/packages/amis-editor/src/plugin/Carousel.tsx +++ b/packages/amis-editor/src/plugin/Carousel.tsx @@ -301,19 +301,20 @@ export class CarouselPlugin extends BasePlugin { }), { type: 'ae-switch-more', - bulk: true, mode: 'normal', name: 'multiple', + bulk: false, label: '多图展示', formType: 'extend', form: { body: [ { - name: 'multiple.count', + name: 'count', label: '数量', type: 'input-number', min: 2, - step: 1 + step: 1, + value: 5 } ] } diff --git a/packages/amis-editor/src/plugin/CollapseGroup.tsx b/packages/amis-editor/src/plugin/CollapseGroup.tsx index 7559c5b947f..42f10bbb98d 100644 --- a/packages/amis-editor/src/plugin/CollapseGroup.tsx +++ b/packages/amis-editor/src/plugin/CollapseGroup.tsx @@ -166,11 +166,15 @@ export class CollapseGroupPlugin extends BasePlugin { getSchemaTpl('icon', { name: 'expandIcon', label: '图标', + value: { + type: 'icon', + vendor: '' + }, pipeIn: (value: any) => value?.icon, pipeOut: (value: any) => ({ type: 'icon', vendor: '', - icon: value + icon: value ? value : undefined }) }) ] diff --git a/packages/amis-editor/src/plugin/Form/InputRating.tsx b/packages/amis-editor/src/plugin/Form/InputRating.tsx index 2c3f79ec7fe..c9ac2e0ade6 100644 --- a/packages/amis-editor/src/plugin/Form/InputRating.tsx +++ b/packages/amis-editor/src/plugin/Form/InputRating.tsx @@ -253,9 +253,7 @@ export class RateControlPlugin extends BasePlugin { { type: 'ae-switch-more', label: '自定义', - bulk: true, mode: 'normal', - value: false, formType: 'extend', form: { body: [ @@ -265,22 +263,6 @@ export class RateControlPlugin extends BasePlugin { name: 'char' } ] - }, - pipeIn: (value: string) => { - if (typeof value === 'string' && value.length) { - return { - character: value - }; - } - return undefined; - }, - pipeOut: (value: any) => { - if (!isObject(value)) { - return undefined; - } - return typeof value.character === 'string' - ? value.character - : undefined; } }, diff --git a/packages/amis-editor/src/plugin/Form/InputTable.tsx b/packages/amis-editor/src/plugin/Form/InputTable.tsx index 313dc521614..ccb2812cda4 100644 --- a/packages/amis-editor/src/plugin/Form/InputTable.tsx +++ b/packages/amis-editor/src/plugin/Form/InputTable.tsx @@ -1187,6 +1187,8 @@ export class TableControlPlugin extends BasePlugin { '确认模式', '开启时,新增、编辑需要点击表格右侧的“保存”按钮才能变更组件数据。未开启时,新增、编辑、删除操作直接改变组件数据。' ), + isChecked: (v: any) => v !== false, + falseValue: false, mode: 'normal', formType: 'extend', hiddenOnDefault: true, diff --git a/packages/amis-editor/src/plugin/Form/InputTree.tsx b/packages/amis-editor/src/plugin/Form/InputTree.tsx index 3db3f554d31..ee8e9c7a082 100644 --- a/packages/amis-editor/src/plugin/Form/InputTree.tsx +++ b/packages/amis-editor/src/plugin/Form/InputTree.tsx @@ -629,6 +629,10 @@ export class TreeControlPlugin extends BasePlugin { bulk: false, name: 'itemActions', formType: 'extend', + defaultData: { + type: 'container', + body: [{type: 'button', label: '按钮'}] + }, form: { body: [ { @@ -755,8 +759,7 @@ export class TreeControlPlugin extends BasePlugin { label: '设置层级', name: 'unfoldedLevel', value: 1, - min: 0, - hiddenOn: 'this.initiallyOpen' + min: 0 } ] } diff --git a/packages/amis-editor/src/plugin/Form/ListSelect.tsx b/packages/amis-editor/src/plugin/Form/ListSelect.tsx index a6c8f314a6f..35ec1111671 100644 --- a/packages/amis-editor/src/plugin/Form/ListSelect.tsx +++ b/packages/amis-editor/src/plugin/Form/ListSelect.tsx @@ -179,8 +179,6 @@ export class ListControlPlugin extends BasePlugin { type: 'ae-switch-more', mode: 'normal', label: '自定义显示模板', - bulk: false, - name: 'itemSchema', formType: 'extend', form: { body: [ diff --git a/packages/amis-editor/src/plugin/Form/Picker.tsx b/packages/amis-editor/src/plugin/Form/Picker.tsx index 6ea1c916350..43519f3510b 100644 --- a/packages/amis-editor/src/plugin/Form/Picker.tsx +++ b/packages/amis-editor/src/plugin/Form/Picker.tsx @@ -369,28 +369,30 @@ export class PickerControlPlugin extends BasePlugin { type: 'ae-switch-more', mode: 'normal', formType: 'extend', - name: 'overflowConfigSwitch', - pipeIn: (value: any) => !!value, + name: 'overflowConfig', + bulk: false, + isChecked: (v: any) => !!v, label: tipedLabel( '标签收纳', '当值数量超出一定数量时,可进行收纳显示' ), + extendData: ['embed'], form: { body: [ { type: 'input-number', - name: 'overflowConfig.maxTagCount', + name: 'maxTagCount', label: '最大标签数', defaultValue: -1 }, getOverflowTagPopoverTpl({ - namePre: 'overflowConfig.overflowTagPopover', + namePre: 'overflowTagPopover', title: '选择器收纳器', key: 'select', className: 'm-b-sm' }), getOverflowTagPopoverTpl({ - namePre: 'overflowConfig.overflowTagPopoverInCRUD', + namePre: 'overflowTagPopoverInCRUD', title: 'CRUD收纳器', key: 'crud', className: 'm-b-sm', diff --git a/packages/amis-editor/src/plugin/Form/Static.tsx b/packages/amis-editor/src/plugin/Form/Static.tsx index 56bb0dd4288..ceeb7422995 100644 --- a/packages/amis-editor/src/plugin/Form/Static.tsx +++ b/packages/amis-editor/src/plugin/Form/Static.tsx @@ -7,273 +7,33 @@ import { getSchemaTpl, setSchemaTpl, tipedLabel, - RendererPluginEvent + RendererPluginEvent, + diff } from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core'; import {BaseEventContext, BasePlugin} from 'amis-editor-core'; import {EditorNodeType} from 'amis-editor-core'; import {mockValue} from 'amis-editor-core'; -// 快速编辑 -setSchemaTpl('quickEdit', (patch: any, manager: any) => ({ - type: 'ae-switch-more', - mode: 'normal', - name: 'quickEdit', - label: '可快速编辑', - value: false, - hiddenOnDefault: true, - formType: 'extend', - pipeIn: (value: any) => !!value, - trueValue: { - mode: 'popOver' - }, - isChecked: (e: any) => { - const {data, name} = e; - return !!get(data, name); - }, - form: { - body: [ - { - label: '编辑模式', - name: 'quickEdit.mode', - type: 'button-group-select', - inputClassName: 'items-center', - visibleOn: 'this.quickEdit', - pipeIn: defaultValue('popOver'), - options: [ - { - label: '下拉', - value: 'popOver' - }, - { - label: '内嵌', - value: 'inline' - } - ] - }, - getSchemaTpl('icon', { - name: 'quickEdit.icon' - }), - getSchemaTpl('switch', { - name: 'quickEdit.saveImmediately', - label: tipedLabel( - '立即保存', - '开启后修改即提交,而不是标记修改批量提交。' - ), - visibleOn: 'this.quickEdit', - pipeIn: (value: any) => !!value - }), - getSchemaTpl('apiControl', { - name: 'quickEdit.saveImmediately.api', - label: '保存接口', - mode: 'row', - description: - '单独给立即保存配置接口,如果不配置,则默认使用quickSaveItemApi。', - visibleOn: 'this.quickEdit && this.quickEdit.saveImmediately' - }), - { - name: 'quickEdit', - asFormItem: true, - visibleOn: 'this.quickEdit', - mode: 'row', - children: ({value, onChange, data}: any) => { - if (value === true) { - value = {}; - } else if (typeof value === 'undefined') { - value = getVariable(data, 'quickEdit'); - } - value = {...value}; - const originMode = value.mode || 'popOver'; - if (value.mode) { - delete value.mode; - } - const originSaveImmediately = value.saveImmediately; - if (value.saveImmediately) { - delete value.saveImmediately; - } - value = - value.body && ['container', 'wrapper'].includes(value.type) - ? { - // schema中存在容器,用自己的就行 - type: 'wrapper', - body: [], - ...value - } - : { - // schema中不存在容器,打开子编辑器时需要包裹一层 - type: 'wrapper', - body: [ - { - type: 'input-text', - name: data.name, - ...value - } - ] - }; - // todo 多个快速编辑表单模式看来只能代码模式编辑了。 - return ( - { - manager.openSubEditor({ - title: '配置快速编辑类型', - value: value, - onChange: (value: any) => - onChange( - { - ...value, - mode: originMode, - saveImmediately: originSaveImmediately - }, - 'quickEdit' - ) - }); - }} - > - 配置快速编辑 - - ); - } - } - ] - } -})); - -// 查看更多 -setSchemaTpl('morePopOver', (patch: any, manager: any) => ({ - type: 'ae-switch-more', - mode: 'normal', - name: 'popOver', - label: '查看更多展示', - value: false, - hiddenOnDefault: true, - formType: 'extend', - pipeIn: (value: any) => !!value, - form: { - body: [ - { - label: '弹出模式', - name: 'popOver.mode', - type: 'button-group-select', - visibleOn: 'this.popOver', - pipeIn: defaultValue('popOver'), - options: [ - { - label: '浮层', - value: 'popOver' - }, - - { - label: '弹框', - value: 'dialog' - }, - - { - label: '抽屉', - value: 'drawer' - } - ] - }, - { - name: 'popOver.position', - label: '浮层位置', - type: 'select', - visibleOn: - 'this.popOver && (this.popOver.mode === "popOver" || !this.popOver.mode)', - pipeIn: defaultValue('center'), - options: [ - { - label: '目标左上角', - value: 'left-top' - }, - { - label: '目标右上角', - value: 'right-top' - }, - { - label: '目标中部', - value: 'center' - }, - { - label: '目标左下角', - value: 'left-bottom' - }, - { - label: '目标右下角', - value: 'right-bottom' - }, - { - label: '页面左上角', - value: 'fixed-left-top' - }, - { - label: '页面右上角', - value: 'fixed-right-top' - }, - { - label: '页面左下角', - value: 'fixed-left-bottom' - }, - { - label: '页面右下角', - value: 'fixed-right-bottom' - } - ] - }, - { - visibleOn: 'this.popOver', - name: 'popOver', - mode: 'row', - asFormItem: true, - children: ({value, onChange}: any) => { - value = { - type: 'panel', - title: '查看详情', - body: '内容详情', - ...value - }; - - return ( - { - manager.openSubEditor({ - title: '配置查看更多展示内容', - value: value, - onChange: (value: any) => onChange(value, 'quickEdit') - }); - }} - > - 查看更多内容配置 - - ); - } - } - ] - } -})); - // 可复制 setSchemaTpl('copyable', { type: 'ae-switch-more', mode: 'normal', name: 'copyable', label: '可复制', - value: false, hiddenOnDefault: true, + trueValue: true, + bulk: false, formType: 'extend', pipeIn: (value: any) => !!value, form: { body: [ { - label: '复制内容模板', - name: 'copyable.content', + label: '内容模板', + name: 'content', type: 'textarea', mode: 'row', maxRow: 2, - visibleOn: 'this.copyable', description: '默认为当前字段值,可定制。' } ] @@ -344,8 +104,189 @@ export class StaticControlPlugin extends BasePlugin { // value: context?.schema.tpl // 避免默认值丢失 // } }), - getSchemaTpl('quickEdit', {}, this.manager), - getSchemaTpl('morePopOver', {}, this.manager), + { + type: 'ae-switch-more', + mode: 'normal', + name: 'quickEdit', + label: '可快速编辑', + hiddenOnDefault: true, + formType: 'extend', + defaultData: { + mode: 'popOver' + }, + bulk: false, + form: { + body: [ + { + label: '编辑模式', + name: 'mode', + type: 'button-group-select', + inputClassName: 'items-center', + pipeIn: defaultValue('popOver'), + options: [ + { + label: '下拉', + value: 'popOver' + }, + { + label: '内嵌', + value: 'inline' + } + ] + }, + getSchemaTpl('icon', { + name: 'icon' + }), + getSchemaTpl('switch', { + name: 'saveImmediately', + label: tipedLabel( + '立即保存', + '开启后修改即提交,而不是标记修改批量提交。' + ), + visibleOn: 'this.quickEdit', + pipeIn: (value: any) => !!value + }), + getSchemaTpl('apiControl', { + name: 'saveImmediately.api', + label: '保存接口', + mode: 'row', + description: + '单独给立即保存配置接口,如果不配置,则默认使用quickSaveItemApi。', + visibleOn: 'this.saveImmediately' + }), + { + type: 'button', + block: true, + onClick: this.editDetail.bind( + this, + context.id, + 'quickEdit', + (nodeSchema: any) => ({ + type: 'container', + body: [ + { + type: 'input-text', + name: nodeSchema.name + } + ] + }) + ), + label: '配置编辑模板' + } + ] + } + }, + { + type: 'ae-switch-more', + mode: 'normal', + name: 'popOver', + label: '查看更多展示', + hiddenOnDefault: true, + defaultData: { + mode: 'popOver' + }, + formType: 'extend', + bulk: false, + pipeIn: (value: any) => !!value, + form: { + body: [ + { + label: '弹出模式', + name: 'mode', + type: 'button-group-select', + pipeIn: defaultValue('popOver'), + options: [ + { + label: '浮层', + value: 'popOver' + }, + + { + label: '弹框', + value: 'dialog' + }, + + { + label: '抽屉', + value: 'drawer' + } + ] + }, + { + name: 'position', + label: '浮层位置', + type: 'select', + visibleOn: 'this.mode === "popOver" || !this.mode', + pipeIn: defaultValue('center'), + options: [ + { + label: '目标左上角', + value: 'left-top' + }, + { + label: '目标右上角', + value: 'right-top' + }, + { + label: '目标中部', + value: 'center' + }, + { + label: '目标左下角', + value: 'left-bottom' + }, + { + label: '目标右下角', + value: 'right-bottom' + }, + { + label: '页面左上角', + value: 'fixed-left-top' + }, + { + label: '页面右上角', + value: 'fixed-right-top' + }, + { + label: '页面左下角', + value: 'fixed-left-bottom' + }, + { + label: '页面右下角', + value: 'fixed-right-bottom' + } + ] + }, + { + type: 'button', + block: true, + onClick: this.editDetail.bind( + this, + context.id, + 'popOver', + (schema: any) => ({ + type: 'panel', + title: '查看详情', + body: [ + { + type: 'tpl', + tpl: '${' + schema.name + '}', + wrapperComponent: '', + inline: true, + editorSetting: { + mock: { + tpl: '内容详情' + } + } + } + ] + }) + ), + label: '查看更多内容配置' + } + ] + } + }, getSchemaTpl('copyable'), getSchemaTpl('labelRemark'), getSchemaTpl('remark'), @@ -417,6 +358,44 @@ export class StaticControlPlugin extends BasePlugin { return props; } + editDetail(id: string, field: string, defaultSchema: (schema: any) => any) { + const manager = this.manager; + const store = manager.store; + const node = store.getNodeById(id); + const value = store.getValueOf(id); + const data = value[field]; + + let originValue = data.type + ? ['container', 'wrapper'].includes(data.type) + ? data + : { + // schema中存在容器,用自己的就行 + type: 'container', + body: [data] + } + : defaultSchema(value); + + node && + value && + this.manager.openSubEditor({ + title: '配置显示模板', + value: originValue, + // slot: { + // type: 'container', + // body: '$$' + // }, + onChange: (newValue: any) => { + newValue = {...originValue, [field]: newValue}; + manager.panelChangeValue(newValue, diff(originValue, newValue)); + }, + data: { + [value.labelField || 'label']: '选项名', + [value.valueField || 'value']: '选项值', + item: '假数据' + } + }); + } + // 事件定义 events: RendererPluginEvent[] = [ { diff --git a/packages/amis-editor/src/plugin/Form/Switch.tsx b/packages/amis-editor/src/plugin/Form/Switch.tsx index 6545d73da25..e961402b2b2 100644 --- a/packages/amis-editor/src/plugin/Form/Switch.tsx +++ b/packages/amis-editor/src/plugin/Form/Switch.tsx @@ -145,7 +145,6 @@ export class SwitchControlPlugin extends BasePlugin { }, { type: 'ae-switch-more', - hiddenOnDefault: false, mode: 'normal', label: '值格式', formType: 'extend', diff --git a/packages/amis-editor/src/plugin/Form/TreeSelect.tsx b/packages/amis-editor/src/plugin/Form/TreeSelect.tsx index 828e0f521ab..2f0a72f862b 100644 --- a/packages/amis-editor/src/plugin/Form/TreeSelect.tsx +++ b/packages/amis-editor/src/plugin/Form/TreeSelect.tsx @@ -82,6 +82,17 @@ export class TreeSelectControlPlugin extends BasePlugin { mode: 'normal' } }; + defaultItemAction = { + type: 'container', + body: [ + { + type: 'button', + icon: 'fa fa-plus', + level: 'link', + size: 'xs' + } + ] + }; notRenderFormZone = true; @@ -464,6 +475,7 @@ export class TreeSelectControlPlugin extends BasePlugin { bulk: false, name: 'itemActions', formType: 'extend', + defaultData: this.defaultItemAction, form: { body: [ { @@ -475,20 +487,6 @@ export class TreeSelectControlPlugin extends BasePlugin { label: '配置自定义操作模板' } ] - }, - pipeIn: (value: any) => { - return value !== undefined; - }, - pipeOut: (value: any) => { - if (value === true) { - return { - type: 'button', - icon: 'fa fa-plus', - level: 'link', - size: 'xs' - }; - } - return value ? value : undefined; } } ] @@ -509,7 +507,7 @@ export class TreeSelectControlPlugin extends BasePlugin { '选项值包含父节点', '开启后对应节点值会包含父节点' ), - value: false, + falseValue: false, formType: 'extend', autoFocus: false, form: { @@ -528,7 +526,6 @@ export class TreeSelectControlPlugin extends BasePlugin { mode: 'normal', name: 'hideRoot', label: '显示顶级节点', - value: true, trueValue: false, falseValue: true, formType: 'extend', @@ -578,7 +575,6 @@ export class TreeSelectControlPlugin extends BasePlugin { '自定义展开层级', '默认展开所有节点层级,开启后可自定义展开层级数' ), - value: true, trueValue: false, falseValue: true, formType: 'extend', @@ -590,8 +586,7 @@ export class TreeSelectControlPlugin extends BasePlugin { label: '设置层级', name: 'unfoldedLevel', value: 1, - min: 0, - hiddenOn: 'this.initiallyOpen' + min: 0 } ] } @@ -733,24 +728,25 @@ export class TreeSelectControlPlugin extends BasePlugin { const store = manager.store; const node = store.getNodeById(id); const value = store.getValueOf(id); - const defaultItemSchema = { - type: 'button', - icon: 'fa fa-plus', - level: 'link', - size: 'xs' - }; + const defaultItemSchema = this.defaultItemAction; + let originData = value.itemActions; + + if (originData.type && originData.type !== 'container') { + originData = { + type: 'container', + body: [originData] + }; + } else { + originData = originData ?? defaultItemSchema; + } node && value && this.manager.openSubEditor({ title: '配置自定义操作模板', - value: schemaToArray(value.itemActions ?? defaultItemSchema), - slot: { - type: 'container', - body: '$$' - }, + value: originData, onChange: (newValue: any) => { - newValue = {...value, itemActions: schemaArrayFormat(newValue)}; + newValue = {...value, itemActions: newValue}; manager.panelChangeValue(newValue, diff(value, newValue)); } }); diff --git a/packages/amis-editor/src/plugin/Image.tsx b/packages/amis-editor/src/plugin/Image.tsx index 61562bcd18f..f4bdcbcf5db 100644 --- a/packages/amis-editor/src/plugin/Image.tsx +++ b/packages/amis-editor/src/plugin/Image.tsx @@ -206,36 +206,6 @@ export class ImagePlugin extends BasePlugin { } ] }, - // 通过外观的尺寸来设置 - // { - // name: 'width', - // label: '宽度', - // type: 'input-number', - // onChange: (value: any) => { - // const node = context.node; - // node.updateState({ - // width: value - // }); - // requestAnimationFrame(() => { - // node.calculateHighlightBox(); - // }); - // } - // }, - // { - // name: 'height', - // label: '高度', - // type: 'input-number', - // onChange: (value: any) => { - // const node = context.node; - // node.updateState({ - // height: value - // }); - // requestAnimationFrame(() => { - // node.calculateHighlightBox(); - // }); - // } - // }, - isUnderField ? null : getSchemaTpl('imageUrl', { diff --git a/packages/amis-editor/src/plugin/List.tsx b/packages/amis-editor/src/plugin/List.tsx index ffb6c754d11..e9be6f1f977 100644 --- a/packages/amis-editor/src/plugin/List.tsx +++ b/packages/amis-editor/src/plugin/List.tsx @@ -120,6 +120,7 @@ export class ListPlugin extends BasePlugin { formType: 'extend', label: '头部', name: 'showHeader', + falseValue: false, // 组件渲染时默认值用的true,所以关闭时置为false而不是删除属性 form: { body: [ { @@ -143,6 +144,7 @@ export class ListPlugin extends BasePlugin { formType: 'extend', label: '底部', name: 'showFooter', + falseValue: false, // 组件渲染时默认值用的true,所以关闭时置为false而不是删除属性 form: { body: [ { diff --git a/packages/amis-editor/src/plugin/Mapping.tsx b/packages/amis-editor/src/plugin/Mapping.tsx index ee5624e0775..2f422d19410 100644 --- a/packages/amis-editor/src/plugin/Mapping.tsx +++ b/packages/amis-editor/src/plugin/Mapping.tsx @@ -76,6 +76,7 @@ export class MappingPlugin extends BasePlugin { bulk: false, name: 'itemSchema', formType: 'extend', + defaultData: this.scaffold.itemSchema, form: { body: [ { diff --git a/packages/amis-editor/src/plugin/Table.tsx b/packages/amis-editor/src/plugin/Table.tsx index fff75f1a2e1..7502aad8960 100644 --- a/packages/amis-editor/src/plugin/Table.tsx +++ b/packages/amis-editor/src/plugin/Table.tsx @@ -575,6 +575,7 @@ export class TablePlugin extends BasePlugin { label: '头部', name: 'showHeader', pipeIn: (value: any) => value ?? true, + falseValue: false, // 这个属性模式按true处理,关闭不能删除,除非去掉配置的header form: { body: [ { @@ -599,6 +600,7 @@ export class TablePlugin extends BasePlugin { label: '底部', name: 'showFooter', pipeIn: (value: any) => value ?? true, + falseValue: false, // 这个属性模式按true处理,关闭不能删除,除非去掉配置的footer form: { body: [ { diff --git a/packages/amis-editor/src/plugin/Table2.tsx b/packages/amis-editor/src/plugin/Table2.tsx index 2309199a451..c94b61cc328 100644 --- a/packages/amis-editor/src/plugin/Table2.tsx +++ b/packages/amis-editor/src/plugin/Table2.tsx @@ -34,6 +34,9 @@ import type {SchemaObject} from 'amis'; import type {IFormItemStore, IFormStore} from 'amis-core'; import type {EditorManager} from 'amis-editor-core'; import {getActionCommonProps} from '../renderer/event-control/helper'; +import cloneDeep from 'lodash/cloneDeep'; +import {addSchema2Toolbar, deepRemove} from './CRUD2/utils'; +import {is} from '@babel/types'; export const Table2RenderereEvent: RendererPluginEvent[] = [ { @@ -327,10 +330,13 @@ export type Table2DynamicControls = Partial< | 'quickSaveItemApi' | 'draggable' | 'itemDraggableOn' - | 'saveOrderApi' - | 'columnTogglable', + | 'saveOrderApi', (context: BaseEventContext) => any - > + > & + Record< + 'columnTogglable', + (context: BaseEventContext, isCRUDContext: boolean) => any + > >; export class Table2Plugin extends BasePlugin { @@ -604,7 +610,6 @@ export class Table2Plugin extends BasePlugin { ]; } - props.expandable.keyField = 'id'; props.expandable.expandedRowKeys = [1]; } @@ -810,14 +815,14 @@ export class Table2Plugin extends BasePlugin { rowSelectionKeyField: context => { return { type: 'input-text', - name: 'rowSelection.keyField', + name: 'keyField', label: '数据源key' }; }, expandableKeyField: context => { return { type: 'input-text', - name: 'rowSelection.keyField', + name: 'keyField', label: '数据源key' }; }, @@ -842,7 +847,45 @@ export class Table2Plugin extends BasePlugin { } }); }, - columnTogglable: context => false + columnTogglable: (context: BaseEventContext, isCRUDContext: boolean) => { + return getSchemaTpl('switch', { + name: 'columnsTogglable', + label: tipedLabel('自定义显示列', '自动即列数量大于10自动开启。'), + onChange: ( + value: boolean, + oldValue: boolean, + model: IFormItemStore, + form: IFormStore + ) => { + // 单独table2使用时不需要放到顶部容器,由table2内部自己处理即可 + if (!isCRUDContext) { + return undefined; + } + + const schema = cloneDeep(form.data); + if (value === true) { + addSchema2Toolbar( + schema, + {type: 'column-toggler', btnClassName: 'm-l-xs'}, + 'header', + 'right' + ); + } else { + deepRemove( + schema.headerToolbar, + item => item.type === 'column-toggler' + ); + schema.columns.forEach((item: any) => { + if (item.toggled !== undefined) { + delete item.toggled; + } + }); + } + form.setValues(schema); + return undefined; + } + }); + } }; /** 需要动态控制的控件 */ @@ -963,40 +1006,13 @@ export class Table2Plugin extends BasePlugin { { title: '列设置', body: [ - dc?.columnTogglable?.(context), + dc?.columnTogglable?.(context, isCRUDContext), getSchemaTpl('switch', { name: 'resizable', label: tipedLabel('可调整列宽', '用户可通过拖拽调整列宽度'), pipeIn: (value: any) => !!value, pipeOut: (value: any) => value - }), - isCRUDContext - ? null - : { - type: 'ae-Switch-More', - mode: 'normal', - name: 'columnsTogglable', - hiddenOnDefault: true, - formType: 'extend', - label: tipedLabel( - '自定义显示列', - '自动即列数量大于10自动开启。' - ), - pipeOut: (value: any) => { - if (value && value.columnsTogglable) { - return {columnsTogglable: {type: 'column-toggler'}}; - } - return value; - }, - form: { - body: [ - { - mode: 'normal', - type: 'ae-columnControl' - } - ] - } - } + }) ].filter(Boolean) }, { @@ -1009,6 +1025,7 @@ export class Table2Plugin extends BasePlugin { label: '可选择', hiddenOnDefault: true, formType: 'extend', + bulk: false, form: { body: [ /** 如果为 CRUD 背景下,主键配置、选择类型在 CRUD 面板中,此处应该隐藏 */ @@ -1018,7 +1035,7 @@ export class Table2Plugin extends BasePlugin { isCRUDContext ? null : { - name: 'rowSelection.type', + name: 'type', label: '选择类型', type: 'button-group-select', options: [ @@ -1049,19 +1066,19 @@ export class Table2Plugin extends BasePlugin { } }, getSchemaTpl('switch', { - name: 'rowSelection.fixed', + name: 'fixed', label: '固定选择列' }), { type: 'input-number', - name: 'rowSelection.columnWidth', + name: 'columnWidth', label: '选择列列宽', min: 0, pipeOut: (data: number) => data || undefined }, { label: '可选区域', - name: 'rowSelection.rowClick', + name: 'rowClick', type: 'button-group-select', value: false, options: [ @@ -1076,11 +1093,11 @@ export class Table2Plugin extends BasePlugin { ] }, getSchemaTpl('formulaControl', { - name: 'rowSelection.disableOn', + name: 'disableOn', label: '行禁用条件' }), { - name: 'rowSelection.selections', + name: 'selections', label: '选择菜单项', type: 'checkboxes', joinValues: false, @@ -1131,13 +1148,14 @@ export class Table2Plugin extends BasePlugin { label: '可展开', hiddenOnDefault: true, formType: 'extend', + bulk: false, form: { body: [ dc?.expandableKeyField?.(context), { type: 'select', label: '展开按钮位置', - name: 'expandable.position', + name: 'position', options: [ { label: '默认', @@ -1158,7 +1176,7 @@ export class Table2Plugin extends BasePlugin { ] }, getSchemaTpl('formulaControl', { - name: 'expandable.expandableOn', + name: 'expandableOn', visibleOn: 'this.expandable', label: '可展开条件' }), @@ -1174,21 +1192,18 @@ export class Table2Plugin extends BasePlugin { data, form }: any) => { - const newValue = { - ...value, - ...(value && value.type - ? {} - : { - type: 'container', - body: [ - { - type: 'tpl', - tpl: '展开行内容', - inline: false - } - ] - }) - }; + const newValue = value?.type + ? value + : { + type: 'container', + body: [ + { + type: 'tpl', + tpl: '展开行内容', + inline: false + } + ] + }; return ( { - onBulkChange({ - [name]: value - }); + onBulkChange(value); }, data: { ...this.manager.store.ctx @@ -1237,22 +1250,18 @@ export class Table2Plugin extends BasePlugin { mode: 'normal', formType: 'extend', bulk: true, - defaultData: { - itemBadge: { - mode: 'dot' - } - }, - isChecked: (e: any) => { - const {data, name} = e; - return data[name]; - }, form: { body: [ { type: 'ae-badge', label: false, name: 'itemBadge', - contentsOnly: true + node: context.node, + contentsOnly: true, + value: { + mode: 'dot', + offset: [0, 0] + } } ] } diff --git a/packages/amis-editor/src/plugin/TableCell2.tsx b/packages/amis-editor/src/plugin/TableCell2.tsx index 30363137962..d0a1e234394 100644 --- a/packages/amis-editor/src/plugin/TableCell2.tsx +++ b/packages/amis-editor/src/plugin/TableCell2.tsx @@ -19,6 +19,8 @@ import { import {remarkTpl} from '../component/BaseControl'; import type {DSField} from '../builder'; +import {schemaToArray} from '../util'; +import omit from 'lodash/omit'; export type TableCell2DynamicControls = Partial< Record< @@ -283,31 +285,23 @@ export class TableCell2Plugin extends BasePlugin { label: '弹出框', type: 'ae-switch-more', hidden: this._isOpColumn, + bulk: false, mode: 'normal', formType: 'extend', - bulk: true, defaultData: { - popOver: { - mode: 'popOver' - } - }, - trueValue: { mode: 'popOver', body: [ { type: 'tpl', - tpl: '弹出框内容' + tpl: '弹出框内容', + wrapperComponent: '' } ] }, - isChecked: (e: any) => { - const {data, name} = e; - return get(data, name); - }, form: { body: [ { - name: 'popOver.mode', + name: 'mode', type: 'button-group-select', label: '模式', value: 'popOver', @@ -327,15 +321,15 @@ export class TableCell2Plugin extends BasePlugin { ] }, getSchemaTpl('formItemSize', { - name: 'popOver.size', + name: 'size', clearValueOnHidden: true, - visibleOn: 'popOver.mode !== "popOver"' + visibleOn: 'mode !== "popOver"' }), { type: 'select', - name: 'popOver.position', + name: 'position', label: '弹出位置', - visibleOn: 'popOver.mode === "popOver"', + visibleOn: 'mode === "popOver"', options: [ 'center', 'left-top', @@ -346,7 +340,7 @@ export class TableCell2Plugin extends BasePlugin { clearValueOnHidden: true }, { - name: 'popOver.trigger', + name: 'trigger', type: 'button-group-select', label: '触发方式', options: [ @@ -362,50 +356,31 @@ export class TableCell2Plugin extends BasePlugin { pipeIn: defaultValue('click') }, getSchemaTpl('switch', { - name: 'popOver.showIcon', - label: '显示图标', - value: true + name: 'showIcon', + label: '显示图标' }), { type: 'input-text', - name: 'popOver.title', + name: 'title', label: '标题' }, { - name: 'popOver.body', + name: 'body', asFormItem: true, label: false, - children: ({value, onBulkChange, onChange, name, data}: any) => { - value = { - body: - value && value.body - ? value.body - : [ - { - type: 'tpl', - tpl: '弹出框内容' - } - ] - }; - + children: ({value: originValue, onChange}: any) => { return ( { this.manager.openSubEditor({ title: '配置弹出框', - value: value, - onChange: value => { - onChange( - value - ? Array.isArray(value) - ? value - : value?.body - ? value.body - : [] - : [] - ); - } + value: schemaToArray(originValue), + slot: { + type: 'container', + body: '$$' + }, + onChange }); }} > @@ -422,26 +397,22 @@ export class TableCell2Plugin extends BasePlugin { }; }, /** 快速编辑 */ - quickEdit: () => { + quickEdit: context => { return { name: 'quickEdit', label: tipedLabel('快速编辑', '输入框左侧或右侧的附加挂件'), type: 'ae-switch-more', hidden: this._isOpColumn, mode: 'normal', + bulk: false, formType: 'extend', - bulk: true, - trueValue: { + defaultData: { mode: 'popOver' }, - isChecked: (e: any) => { - const {data, name} = e; - return !!get(data, name); - }, form: { body: [ { - name: 'quickEdit.mode', + name: 'mode', type: 'button-group-select', label: '模式', value: 'popOver', @@ -458,11 +429,11 @@ export class TableCell2Plugin extends BasePlugin { }, getSchemaTpl('icon', { - name: 'quickEdit.icon' + name: 'icon' }), getSchemaTpl('switch', { - name: 'quickEdit.saveImmediately', + name: 'saveImmediately', label: tipedLabel( '修改立即保存', '开启后修改即提交,而不是批量提交,需要配置快速保存接口用于提交数据' @@ -474,65 +445,48 @@ export class TableCell2Plugin extends BasePlugin { label: '立即保存接口', description: '默认使用表格的「快速保存单条」接口,若单独给立即保存配置接口,则优先使用局部配置。', - name: 'quickEdit.saveImmediately.api', - visibleOn: 'this.quickEdit && this.quickEdit.saveImmediately' + name: 'saveImmediately.api', + visibleOn: 'this.saveImmediately' }), { - name: 'quickEdit', asFormItem: true, label: false, - children: ({value, onBulkChange, name, data}: any) => { - if (value === true) { - value = {}; - } else if (typeof value === 'undefined') { - value = getVariable(data, 'quickEdit'); - } - value = {...value}; - const originMode = value.mode || 'popOver'; - if (value.mode) { - delete value.mode; - } - const originSaveImmediately = value.saveImmediately; - if (value.saveImmediately) { - delete value.saveImmediately; - } - value = - value.body && ['container', 'wrapper'].includes(value.type) - ? { - // schema中存在容器,用自己的就行 - type: 'wrapper', - body: [], - ...value - } - : { - // schema中不存在容器,打开子编辑器时需要包裹一层 - type: 'wrapper', - body: [ - { - type: 'input-text', - name: data.name, - ...value - } - ] - }; - + children: ({onBulkChange}: any) => { // todo 多个快速编辑表单模式看来只能代码模式编辑了。 return ( { + let data = context.node.schema.quickEdit + ? omit(context.node.schema.quickEdit, [ + 'saveImmediately', + 'icon', + 'mode' + ]) + : {}; + let originValue = data?.type + ? ['container', 'wrapper'].includes(data.type) + ? data + : { + // schema中存在容器,用自己的就行 + type: 'container', + body: [data] + } + : { + type: 'container', + body: [ + { + type: 'input-text', + name: context.node.schema.name + } + ] + }; + this.manager.openSubEditor({ title: '配置快速编辑类型', - value: value, - onChange: value => - onBulkChange({ - [name]: { - ...value, - mode: originMode, - saveImmediately: originSaveImmediately - } - }) + value: originValue, + onChange: value => onBulkChange(value) }); }} > @@ -697,13 +651,13 @@ export class TableCell2Plugin extends BasePlugin { mode: 'normal', name: 'copyable', label: '可复制', - hiddenOnDefault: true, + trueValue: true, formType: 'extend', + bulk: false, form: { body: [ { - name: 'copyable.content', - visibleOn: 'this.copyable', + name: 'content', type: 'ae-formulaControl', label: '复制内容' } diff --git a/packages/amis-editor/src/plugin/Timeline.tsx b/packages/amis-editor/src/plugin/Timeline.tsx index ad1a7d7b2e4..c2b6ec51aae 100644 --- a/packages/amis-editor/src/plugin/Timeline.tsx +++ b/packages/amis-editor/src/plugin/Timeline.tsx @@ -95,6 +95,21 @@ export class TimelinePlugin extends BasePlugin { bulk: false, name: 'itemTitleSchema', formType: 'extend', + defaultData: { + type: 'container', + body: [ + { + type: 'tpl', + tpl: '${label}', + editorSetting: { + mock: { + tpl: '节点标题' + } + }, + wrapperComponent: '' + } + ] + }, form: { body: [ { diff --git a/packages/amis-editor/src/renderer/SwitchMoreControl.tsx b/packages/amis-editor/src/renderer/SwitchMoreControl.tsx index ef377073560..9482f58b726 100644 --- a/packages/amis-editor/src/renderer/SwitchMoreControl.tsx +++ b/packages/amis-editor/src/renderer/SwitchMoreControl.tsx @@ -18,6 +18,7 @@ import type {FormSchema} from 'amis'; import type {FormControlProps} from 'amis-core'; import fromPairs from 'lodash/fromPairs'; import some from 'lodash/some'; +import pick from 'lodash/pick'; export interface SwitchMoreProps extends FormControlProps { className?: string; @@ -36,12 +37,18 @@ export interface SwitchMoreProps extends FormControlProps { overlay?: boolean; container?: HTMLElement | (() => HTMLElement); target?: React.ReactNode | Function; - trueValue?: any; // 开关开启时匹配的 value, 默认 true - falseValue?: any; // 开关关闭时匹配的 value, 默认 flase // editable?: boolean; removable?: boolean; // 是否可删除此项配置 hiddenOnDefault?: boolean; // bulk且不配置时 默认收起 - bulk?: boolean; // 是否是一个综合object属性,若是,最终提交所有项覆盖到表单data,否则提交为 [name] 一项, + /** + * + * bulk是指extend的内容是和name为平级还是子级,会产生几种情况: + * 1. 有name 且bulk为false时,代表这个属性不是一个boolean值,而是一个object,有值=开启,无值=关闭 {kaiguan: {extend: xxx}} + * 2. 有name 且bulk为true时,代表这个属性本身是开关,但还有其他同级别相关属性放在扩展中,因此需要bulk更新方式进行批量更新 {kaiguan: true, extend: xxx} + * 3. 没有name 且bulk为true时,代表没有属性对应这个开关,开关只是为了表达配置交互层面的收纳含义 {extend: xxx} + * 注意:不会出现没有name 且bulk为false的情况 + */ + bulk?: boolean; onRemove?: (e: React.UIEvent | void) => void; onClose: (e: React.UIEvent | void) => void; clearChildValuesOnOff?: boolean; // 关闭开关时,删除子表单字段,默认 true @@ -52,6 +59,8 @@ export interface SwitchMoreProps extends FormControlProps { name?: string; bulk?: boolean; }) => boolean; + trueValue?: any; // 开关开启时name对应的值,当isChecked属性不配置时,也会通过这个匹配是不是开启状态 + falseValue?: any; // 开关关闭时name对应的值 } interface SwitchMoreState { @@ -64,6 +73,11 @@ interface SwitchMoreState { * 是否开启编辑 */ checked: boolean; + + /** + * 子表单的数据key + */ + childFormNames: string[]; } export default class SwitchMore extends React.Component< @@ -93,8 +107,6 @@ export default class SwitchMore extends React.Component< // placement: 'left', overlay: true, rootClose: false, - trueValue: true, - falseValue: false, formType: 'pop', bulk: true, clearChildValuesOnOff: true @@ -110,44 +122,37 @@ export default class SwitchMore extends React.Component< } initState() { - const { - data, - value, - trueValue, - falseValue, - name, - bulk, - hiddenOnDefault, - isChecked - } = this.props; + const {data, value, trueValue, name, bulk, hiddenOnDefault, isChecked} = + this.props; let checked = false; - let show = false; + const formNames = this.getFormItemNames(); + // 优先用传入的是否选中函数来判断 if (isChecked && typeof isChecked === 'function') { checked = isChecked({data, value, name, bulk}); - show = checked; } - // 这个开关 无具体属性对应 - else if (!name) { - // 子表单项是组件根属性,遍历看是否有值 - if (bulk) { - const formNames = this.getFormItemNames(); - checked = some(formNames, key => data[key] !== undefined); - show = checked; - } else { - checked = value === trueValue; - } - } else { - checked = !!value === trueValue; + // 其次使用trueValue,主要是类似于属性值是disableXX之类的反义但配置要改为可用XX等正向含义时,这里会trueValue为false + else if (trueValue != null) { + checked = value === trueValue; + } + // 有属性名,自己本身对应开关 + else if (name) { + checked = data[name] != null && data[name] !== false; + } + // 这个开关 无具体属性对应, 子表单项任意一个开启表示开 + else { + checked = some(formNames, key => data[key] !== undefined); } // 开关有属性对应 return { checked, - show + show: hiddenOnDefault === true ? false : checked, + childFormNames: formNames }; } + // 获取子表单项的内容 getFormItemNames() { const {form} = this.props; @@ -196,36 +201,85 @@ export default class SwitchMore extends React.Component< onBulkChange, onChange, bulk, - defaultData, name, + defaultData, trueValue, falseValue, clearChildValuesOnOff } = this.props; - this.setState({checked}); + if (name) { + let newValue = checked + ? this.getInitTureValue() + : falseValue ?? undefined; + + onChange(newValue); + } - // 子表单项是组件根属性,用bulk处理所有属性 + // 子表单项是同级别的需要单独更新一下 if (bulk) { - // 选中后,给一个默认 {} 或 配置的默认值 + // 选中情况下,值需要进行更新 if (checked) { - let data = defaultData ? {...defaultData} : {}; - name && (data[name] = trueValue); - onBulkChange && onBulkChange(data); + let newValue = defaultData ?? trueValue; + newValue && onBulkChange && onBulkChange(newValue); } - // 取消选中后,将所有字段重置 - else { - const values = clearChildValuesOnOff - ? fromPairs(this.getFormItemNames().map(i => [i, undefined])) - : {}; - name && (values[name] = falseValue); - onBulkChange && onBulkChange(values); + // 这个逻辑感觉主要是为了一些本身有默认值的相关配置,不想删除,只是想保留初始 + else if (clearChildValuesOnOff) { + onBulkChange && + onBulkChange( + fromPairs(this.state.childFormNames.map(i => [i, undefined])) + ); } - return; } - onChange(checked ? defaultData || true : undefined); + + this.setState({checked, show: checked}); + } + + /** + * 返回子表单的数据,如果是同级,直接返回当前数据域,否则返回当前数据作为子表单 + */ + getExtendValues() { + const {name, data: ctx, bulk} = this.props; + + if (!ctx) { + return {}; + } + + if (bulk) { + return ctx; + } + + return name ? ctx[name] : {}; + } + + /** + * 打开后,首先遵循默认值设置,之后遵循选中值设置 + * 当都不设置时,要看是否是object类型,是object类型,需要是空对象 + * + * 关闭后,先遵循关闭值设置,否则一切回归原始删除属性状态 + */ + getInitTureValue() { + const {bulk, defaultData, trueValue} = this.props; + + if (defaultData) { + return {...defaultData}; + } + + if (trueValue != null) { + return trueValue; + } + + if (bulk) { + return true; + } + + return {}; } + /** + * 弹窗配置的提交 + * @param values + */ @autobind handleSubmit(values: any) { const {onChange, onBulkChange, bulk} = this.props; @@ -254,7 +308,7 @@ export default class SwitchMore extends React.Component< } renderActions() { - const {render, removable, disabled, form, formType, hiddenOnDefault, bulk} = + const {removable, disabled, form, formType, hiddenOnDefault, render} = this.props; const {checked, show} = this.state; @@ -265,7 +319,7 @@ export default class SwitchMore extends React.Component< const actions = []; if (formType === 'dialog') { actions.push( - render('switch-more-form', this.renderDialogMore(), { + render('switch-more-dialog', this.renderDialogMore(), { key: 'edit', onSubmit: this.handleSubmit }) @@ -283,7 +337,7 @@ export default class SwitchMore extends React.Component< ); - } else if (bulk && hiddenOnDefault && formType === 'extend') { + } else if (hiddenOnDefault && formType === 'extend') { actions.push( { return getSchemaTpl('creatable', { formType: 'extend', autoFocus: false, - hiddenOnDefault: false, ...controlSchema, form: { body: [