diff --git a/packages/amis-editor/src/plugin/Form/InputFile.tsx b/packages/amis-editor/src/plugin/Form/InputFile.tsx index 8bb730a17..442b2a068 100644 --- a/packages/amis-editor/src/plugin/Form/InputFile.tsx +++ b/packages/amis-editor/src/plugin/Form/InputFile.tsx @@ -4,10 +4,7 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core'; import {tipedLabel} from '../../component/BaseControl'; import {ValidatorTag} from '../../validator'; import {getEventControlConfig} from '../../renderer/event-control/helper'; -import { - RendererPluginAction, - RendererPluginEvent -} from 'amis-editor-core'; +import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core'; export class FileControlPlugin extends BasePlugin { // 关联渲染器名字 @@ -25,7 +22,11 @@ export class FileControlPlugin extends BasePlugin { scaffold = { type: 'input-file', label: '文件上传', - name: 'file' + name: 'file', + receiver: { + url: 'object-upload://default', + method: 'post' + } }; previewSchema: any = { type: 'form', @@ -341,7 +342,7 @@ export class FileControlPlugin extends BasePlugin { label: tipedLabel( '文件类型', '请填入文件的后缀,多个类型用,隔开' - ), + ) }, getSchemaTpl('fileUrl', { name: 'templateUrl', diff --git a/packages/amis-editor/src/plugin/Form/InputTree.tsx b/packages/amis-editor/src/plugin/Form/InputTree.tsx index b431ebeb7..06ee9e9d2 100644 --- a/packages/amis-editor/src/plugin/Form/InputTree.tsx +++ b/packages/amis-editor/src/plugin/Form/InputTree.tsx @@ -7,6 +7,8 @@ import { getArgsWrapper, getEventControlConfig } from '../../renderer/event-control/helper'; +import { tipedLabel } from '../../component/BaseControl'; +import {ValidatorTag} from '../../validator'; export class TreeControlPlugin extends BasePlugin { // 关联渲染器名字 @@ -250,130 +252,240 @@ export class TreeControlPlugin extends BasePlugin { ] } }; - panelBodyCreator = (context: BaseEventContext) => - getSchemaTpl('tabs', [ + panelJustify = true; + + panelBodyCreator = (context: BaseEventContext) => { + const renderer: any = context.info.renderer; + return getSchemaTpl('tabs', [ { - title: '常规', - body: [ - /* - getSchemaTpl('switchDefaultValue'), + title: '属性', + body: getSchemaTpl('collapseGroup', [ { - type: 'input-text', - name: 'value', - label: '默认值', - visibleOn: 'typeof this.value !== "undefined"' + title: '基本', + body: [ + getSchemaTpl('formItemName', { + required: true + }), + getSchemaTpl('label'), + getSchemaTpl('multiple', { + body: [ + getSchemaTpl('switch', { + label: tipedLabel( + '子节点自动选', + '当选中父节点时级联选择子节点' + ), + name: 'autoCheckChildren', + value: true + }), + getSchemaTpl('switch', { + label: tipedLabel( + '子节点可反选', + '子节点可反选,值包含父子节点' + ), + name: 'cascade', + hiddenOn: '!data.autoCheckChildren' + }), + getSchemaTpl('switch', { + label: tipedLabel( + '值包含父节点', + '选中父节点时,值里面将包含父子节点的值,否则只会保留父节点的值' + ), + name: 'withChildren', + hiddenOn: '!data.autoCheckChildren && data.cascade' + }), + getSchemaTpl('switch', { + label: tipedLabel( + '值只含子节点', + 'ui 行为级联选中子节点,子节点可反选,值只包含子节点的值' + ), + name: 'onlyChildren', + hiddenOn: '!data.autoCheckChildren' + }), + { + type: 'input-number', + label: '节点最小数', + name: 'minLength' + }, + { + type: 'input-number', + label: '节点最大数', + name: 'maxLength' + } + ] + }), + getSchemaTpl('valueFormula', { + rendererSchema: { + ...context?.schema, + type: 'tree-select' + }, + visibleOn: 'this.options && this.options.length > 0' + }), + + getSchemaTpl('labelRemark'), + getSchemaTpl('remark'), + getSchemaTpl('placeholder'), + getSchemaTpl('description') + ] }, - */ - getSchemaTpl('valueFormula', { - rendererSchema: { - ...context?.schema, - type: 'tree-select' // 改用树形输入框,避免占用太多空间 - }, - mode: 'vertical' // 改成上下展示模式 - }), - getSchemaTpl('fieldSet', { + { title: '选项', body: [ - { - $ref: 'options', - name: 'options' - }, - getSchemaTpl('source', { - sampleBuilder: (schema: any) => - JSON.stringify( + getSchemaTpl('treeOptionControl', { + label: '数据', + otherApiFooter: [ + { + type: 'input-text', + label: '图标字段', + name: 'iconField', + value: 'icon' + } + ] + }), + getSchemaTpl('switch', { + label: '只可选择叶子节点', + name: 'onlyLeaf' + }), + getSchemaTpl('creatable', { + formType: 'extend', + hiddenOnDefault: true, + label: '可新增', + form: { + body: [ + getSchemaTpl('switch', { + label: '顶层可新增', + value: true, + name: 'rootCreatable' + }), { - status: 0, - msg: '', - data: { - options: [ - { - label: '选项A', - value: 'a', - children: [ - { - label: '子选项', - value: 'c' - } - ] - }, - - { - label: '选项B', - value: 'b' - } - ] - } + type: 'input-text', + label: '顶层文案', + value: '添加一级节点', + name: 'rootCreateTip', + hiddenOn: '!data.rootCreatable' }, - null, - 2 - ) + getSchemaTpl('addApi') + ] + } }), - - getSchemaTpl('switch', { - label: '隐藏顶级', - name: 'hideRoot' + getSchemaTpl('editable', { + formType: 'extend', + hiddenOnDefault: true, + form: { + body: [getSchemaTpl('editApi')] + } }), - - getSchemaTpl('switch', { - name: 'showIcon', - label: '是否显示图标', - pipeIn: defaultValue(true) - }), - - getSchemaTpl('multiple'), - - getSchemaTpl('switch', { - name: 'cascade', - label: '不自动选中子节点', - visibleOn: 'data.multiple', - description: '选中父级时,孩子节点是否自动选中' - }), - - getSchemaTpl('switch', { - name: 'withChildren', - label: '数值是否携带子节点', - visibleOn: 'data.cascade !== true && data.multiple', - disabledOn: 'data.onlyChildren' - }), - - getSchemaTpl('switch', { - name: 'onlyChildren', - label: '数值是否只包含子节点', - visibleOn: 'data.cascade !== true && data.multiple', - disabledOn: 'data.withChildren' - }), - - getSchemaTpl('joinValues'), - getSchemaTpl('delimiter'), - getSchemaTpl('extractValue'), - getSchemaTpl('autoFill') + getSchemaTpl('removable', { + formType: 'extend', + hiddenOnDefault: true, + form: { + body: [getSchemaTpl('deleteApi')] + } + }) ] - }) - ] + }, + { + title: '高级', + body: [ + getSchemaTpl('valueFormula', { + name: 'highlightTxt', + label: '高亮节点字符', + type: 'input-text' + }), + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'enableNodePath', + label: tipedLabel('选项值包含父节点', '开启后对应节点值会包含父节点'), + value: false, + formType: 'extend', + form: { + body: [ + { + type: 'input-text', + label: '路径分隔符', + value: '/', + name: 'pathSeparator' + } + ] + } + }, + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'hideRoot', + label: '显示顶级节点', + value: true, + trueValue: false, + falseValue: true, + formType: 'extend', + form: { + body: [ + { + type: 'input-text', + label: '节点文案', + value: '顶级', + name: 'rootLabel' + } + ] + } + }, + getSchemaTpl('switch', { + label: '显示节点图标', + name: 'showIcon', + value: true + }), + getSchemaTpl('switch', { + label: tipedLabel('显示节点勾选框','单选情况下,也可显示树节点勾选框'), + name: 'showRadio', + hiddenOn: 'data.multiple' + }), + getSchemaTpl('switch', { + label: tipedLabel('显示层级展开线', '显示树层级展开线'), + name: 'showOutline' + }), + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'initiallyOpen', + label: tipedLabel('自定义展开层级', '默认展开所有节点层级,开启后可自定义展开层级数'), + value: true, + trueValue: false, + falseValue: true, + formType: 'extend', + form: { + body: [ + { + type: 'input-number', + label: '设置层级', + name: 'unfoldedLevel', + value: 1, + hiddenOn: 'data.initiallyOpen' + } + ] + } + } + ] + }, + getSchemaTpl('status', { + isFormItem: true, + readonly: true + }), + getSchemaTpl('validation', {tag: ValidatorTag.Tree}) + ]) }, { title: '外观', - body: [ - { - label: '顶级文字', - name: 'rootLabel', - type: 'input-text', - pipeIn: defaultValue('顶级'), - visibleOn: 'data.hideRoot !== true' - }, - - getSchemaTpl('switch', { - name: 'showIcon', - label: '是否显示图标', - pipeIn: defaultValue(true) - }), - - getSchemaTpl('switch', { - label: '是否显示单选按钮', - name: 'showRadio', - visibleOn: '!data.multiple' + body: getSchemaTpl('collapseGroup', [ + getSchemaTpl('style:formItem', {renderer}), + getSchemaTpl('style:classNames', { + schema: [ + getSchemaTpl('className', { + label: 'tree容器', + name: 'treeContainerClassName' + }) + ] }) - ] + ]) }, { title: '事件', @@ -386,6 +498,7 @@ export class TreeControlPlugin extends BasePlugin { ] } ]); + }; } registerEditorPlugin(TreeControlPlugin); diff --git a/packages/amis-editor/src/plugin/Form/TreeSelect.tsx b/packages/amis-editor/src/plugin/Form/TreeSelect.tsx index 0b866304e..10a9817ad 100644 --- a/packages/amis-editor/src/plugin/Form/TreeSelect.tsx +++ b/packages/amis-editor/src/plugin/Form/TreeSelect.tsx @@ -1,11 +1,10 @@ -import { - RendererPluginAction, - RendererPluginEvent -} from 'amis-editor-core'; +import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core'; import {defaultValue, getSchemaTpl} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core'; import {BaseEventContext, BasePlugin} from 'amis-editor-core'; import {getEventControlConfig} from '../../renderer/event-control/helper'; +import {ValidatorTag} from '../../validator'; +import {tipedLabel} from '../../component/BaseControl'; export class TreeSelectControlPlugin extends BasePlugin { // 关联渲染器名字 @@ -249,154 +248,262 @@ export class TreeSelectControlPlugin extends BasePlugin { } }; + panelJustify = true; + panelBodyCreator = (context: BaseEventContext) => { - return [ - getSchemaTpl('tabs', [ - { - title: '常规', - body: [ - getSchemaTpl('valueFormula', { - rendererSchema: context?.schema, - mode: 'vertical' // 改成上下展示模式 - }), + const renderer: any = context.info.renderer; + return getSchemaTpl('tabs', [ + { + title: '属性', + body: getSchemaTpl('collapseGroup', [ + { + title: '基本', + body: [ + getSchemaTpl('formItemName', { + required: true + }), + getSchemaTpl('label'), - getSchemaTpl('clearable'), - - getSchemaTpl('hideNodePathLabel'), - - getSchemaTpl('fieldSet', { - title: '选项', - body: [ - { - $ref: 'options', - name: 'options' + getSchemaTpl('clearable'), + getSchemaTpl('searchable'), + getSchemaTpl('multiple', { + body: [ + getSchemaTpl('switch', { + label: tipedLabel( + '子节点自动选', + '当选中父节点时级联选择子节点' + ), + name: 'autoCheckChildren', + value: true + }), + getSchemaTpl('switch', { + label: tipedLabel( + '子节点可反选', + '子节点可反选,值包含父子节点' + ), + name: 'cascade', + hiddenOn: '!data.autoCheckChildren' + }), + getSchemaTpl('switch', { + label: tipedLabel( + '值包含父节点', + '选中父节点时,值里面将包含父子节点的值,否则只会保留父节点的值' + ), + name: 'withChildren', + hiddenOn: '!data.autoCheckChildren && data.cascade' + }), + getSchemaTpl('switch', { + label: tipedLabel( + '值只含子节点', + 'ui 行为级联选中子节点,子节点可反选,值只包含子节点的值' + ), + name: 'onlyChildren', + hiddenOn: '!data.autoCheckChildren' + }), + { + type: 'input-number', + label: '节点最小数', + name: 'minLength' + }, + { + type: 'input-number', + label: '节点最大数', + name: 'maxLength' + } + ] + }), + getSchemaTpl('valueFormula', { + rendererSchema: { + ...context?.schema, + type: 'tree-select' }, + visibleOn: 'this.options && this.options.length > 0' + }), - getSchemaTpl('source', { - sampleBuilder: (schema: any) => - JSON.stringify( - { - status: 0, - msg: '', - data: { - options: [ - { - label: '选项A', - value: 'a', - children: [ - { - label: '子选项', - value: 'c' - } - ] - }, - - { - label: '选项B', - value: 'b' - } - ] - } - }, - null, - 2 - ) - }), - - getSchemaTpl('api', { - name: 'autoComplete', - label: '自动完成接口', - description: - '每次输入新内容后,将调用接口,根据接口返回更新选项。当前用户输入值在 `\\${term}` 中。请不要与获取选项接口同时设置。' - }), - - getSchemaTpl('switch', { - name: 'initiallyOpen', - label: '是否默认展开子选项', - pipeIn: defaultValue(true) - }), - - { - type: 'input-text', - name: 'unfoldedLevel', - label: '选项默认展开级数', - visibleOn: - 'typeof this.initiallyOpen !== "undefined" || !this.initiallyOpen' - }, - - getSchemaTpl('switch', { - name: 'showIcon', - label: '是否显示图标', - pipeIn: defaultValue(true) - }), - - getSchemaTpl('searchable'), - - getSchemaTpl('switch', { - label: '是否显示单选按钮', - name: 'showRadio', - visibleOn: '!data.multiple' - }), - - getSchemaTpl('multiple'), - - getSchemaTpl('switch', { - name: 'cascade', - label: '不自动选中子节点', - visibleOn: 'data.multiple', - description: '选中父级时,孩子节点是否自动选中' - }), - - getSchemaTpl('switch', { - name: 'withChildren', - label: '数值是否携带子节点', - visibleOn: 'data.cascade !== true && data.multiple' - }), - - getSchemaTpl('switch', { - name: 'onlyChildren', - label: '数值是否只包含子节点', - visibleOn: 'data.cascade !== true && data.multiple', - disabledOn: 'data.withChildren' - }), - - getSchemaTpl('joinValues'), - getSchemaTpl('delimiter'), - getSchemaTpl('extractValue'), - getSchemaTpl('autoFill'), - - getSchemaTpl('creatable'), - getSchemaTpl('api', { - label: '新增选项接口', - name: 'addApi' - }), - - getSchemaTpl('editable'), - getSchemaTpl('api', { - label: '编辑选项接口', - name: 'editApi' - }), - - getSchemaTpl('removable'), - getSchemaTpl('api', { - label: '删除选项接口', - name: 'deleteApi' - }) - ] - }) - ] - }, - { - title: '事件', - body: [ - getSchemaTpl('eventControl', { - name: 'onEvent', - ...getEventControlConfig(this.manager, context) - }) - ] - } - ]) - ]; + getSchemaTpl('labelRemark'), + getSchemaTpl('remark'), + getSchemaTpl('placeholder'), + getSchemaTpl('description') + ] + }, + { + title: '选项', + body: [ + getSchemaTpl('treeOptionControl', { + label: '数据', + otherApiFooter: [ + { + type: 'input-text', + label: '图标字段', + name: 'iconField', + value: 'icon' + } + ] + }), + getSchemaTpl('switch', { + label: '只可选择叶子节点', + name: 'onlyLeaf' + }), + getSchemaTpl('creatable', { + formType: 'extend', + hiddenOnDefault: true, + label: '可新增', + form: { + body: [ + getSchemaTpl('switch', { + label: '顶层可新增', + value: true, + name: 'rootCreatable' + }), + { + type: 'input-text', + label: '顶层文案', + value: '添加一级节点', + name: 'rootCreateTip', + hiddenOn: '!data.rootCreatable' + }, + getSchemaTpl('addApi') + ] + } + }), + getSchemaTpl('editable', { + formType: 'extend', + hiddenOnDefault: true, + form: { + body: [getSchemaTpl('editApi')] + } + }), + getSchemaTpl('removable', { + formType: 'extend', + hiddenOnDefault: true, + form: { + body: [getSchemaTpl('deleteApi')] + } + }) + ] + }, + { + title: '高级', + body: [ + getSchemaTpl('valueFormula', { + name: 'highlightTxt', + label: '高亮节点字符', + type: 'input-text' + }), + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'enableNodePath', + label: tipedLabel('选项值包含父节点', '开启后对应节点值会包含父节点'), + value: false, + formType: 'extend', + form: { + body: [ + { + type: 'input-text', + label: '路径分隔符', + value: '/', + name: 'pathSeparator' + } + ] + } + }, + getSchemaTpl('switch', { + label: tipedLabel( + '选项文本仅显示选中节点', + '隐藏选择框中已选中节点的祖先节点的文本信息' + ), + name: 'hideNodePathLabel' + }), + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'hideRoot', + label: '显示顶级节点', + value: true, + trueValue: false, + falseValue: true, + formType: 'extend', + form: { + body: [ + { + type: 'input-text', + label: '节点文案', + value: '顶级', + name: 'rootLabel' + } + ] + } + }, + getSchemaTpl('switch', { + label: '显示节点图标', + name: 'showIcon', + value: true + }), + getSchemaTpl('switch', { + label: tipedLabel('显示节点勾选框','单选情况下,也可显示树节点勾选框'), + name: 'showRadio', + hiddenOn: 'data.multiple' + }), + getSchemaTpl('switch', { + label: tipedLabel('显示层级展开线', '显示树层级展开线'), + name: 'showOutline' + }), + { + type: 'ae-Switch-More', + mode: 'normal', + name: 'initiallyOpen', + label: tipedLabel('自定义展开层级', '默认展开所有节点层级,开启后可自定义展开层级数'), + value: true, + trueValue: false, + falseValue: true, + formType: 'extend', + form: { + body: [ + { + type: 'input-number', + label: '设置层级', + name: 'unfoldedLevel', + value: 1, + hiddenOn: 'data.initiallyOpen' + } + ] + } + } + ] + }, + getSchemaTpl('status', { + isFormItem: true, + readonly: true + }), + getSchemaTpl('validation', {tag: ValidatorTag.Tree}) + ]) + }, + { + title: '外观', + body: getSchemaTpl('collapseGroup', [ + getSchemaTpl('style:formItem', {renderer}), + getSchemaTpl('style:classNames', { + schema: [ + getSchemaTpl('className', { + label: 'tree容器', + name: 'treeContainerClassName' + }) + ] + }) + ]) + }, + { + title: '事件', + className: 'p-none', + body: [ + getSchemaTpl('eventControl', { + name: 'onEvent', + ...getEventControlConfig(this.manager, context) + }) + ] + } + ]); }; } diff --git a/packages/amis-editor/src/renderer/FormulaControl.tsx b/packages/amis-editor/src/renderer/FormulaControl.tsx index e5cf51b1b..be4bbf159 100644 --- a/packages/amis-editor/src/renderer/FormulaControl.tsx +++ b/packages/amis-editor/src/renderer/FormulaControl.tsx @@ -359,9 +359,7 @@ export default class FormulaControl extends React.Component< @autobind renderFormulaValue(item: any) { const html = {__html: item.html}; - { - /* bca-disable-next-line */ - } + // bca-disable-next-line return ; } diff --git a/packages/amis-editor/src/renderer/SwitchMoreControl.tsx b/packages/amis-editor/src/renderer/SwitchMoreControl.tsx index 4448c1f8a..e102358f1 100644 --- a/packages/amis-editor/src/renderer/SwitchMoreControl.tsx +++ b/packages/amis-editor/src/renderer/SwitchMoreControl.tsx @@ -35,8 +35,8 @@ export interface SwitchMoreProps extends FormControlProps { overlay?: boolean; container?: React.ReactNode | Function; target?: React.ReactNode | Function; - trueValue?: any; - falseValue?: any; + trueValue?: any; // 开关开启时匹配的 value, 默认 true + falseValue?: any; // 开关关闭时匹配的 value, 默认 flase // editable?: boolean; removable?: boolean; // 是否可删除此项配置 hiddenOnDefault?: boolean; // bulk且不配置时 默认收起 @@ -100,7 +100,7 @@ export default class SwitchMore extends React.Component< } initState() { - const {data, value, name, bulk, hiddenOnDefault} = this.props; + const {data, value, trueValue, falseValue, name, bulk, hiddenOnDefault} = this.props; let checked = false; let show = false; @@ -112,10 +112,10 @@ export default class SwitchMore extends React.Component< checked = some(formNames, key => data[key] !== undefined); show = checked; } else { - checked = value != null; + checked = value === trueValue; } } else { - checked = !!value; + checked = !!value === trueValue; } // 开关有属性对应 @@ -169,7 +169,7 @@ export default class SwitchMore extends React.Component< @autobind handleSwitchChange(checked: boolean) { - const {onBulkChange, onChange, bulk, defaultData, name} = this.props; + const {onBulkChange, onChange, bulk, defaultData, name, trueValue, falseValue} = this.props; this.setState({checked}); @@ -178,7 +178,7 @@ export default class SwitchMore extends React.Component< // 选中后,给一个默认 {} 或 配置的默认值 if (checked) { let data = defaultData ? {...defaultData} : {}; - name && (data[name] = true); + name && (data[name] = trueValue); onBulkChange && onBulkChange(data); } // 取消选中后,讲所有字段重置 @@ -186,7 +186,7 @@ export default class SwitchMore extends React.Component< const values = fromPairs( this.getFormItemNames().map(i => [i, undefined]) ); - name && (values[name] = false); + name && (values[name] = falseValue); onBulkChange && onBulkChange(values); } return; diff --git a/packages/amis-editor/src/renderer/TreeOptionControl.tsx b/packages/amis-editor/src/renderer/TreeOptionControl.tsx index 8989dcb8e..3648b5fdd 100644 --- a/packages/amis-editor/src/renderer/TreeOptionControl.tsx +++ b/packages/amis-editor/src/renderer/TreeOptionControl.tsx @@ -24,12 +24,13 @@ import {tipedLabel} from '../component/BaseControl'; import type {Option} from 'amis'; import type {FormControlProps} from 'amis-core'; -import {SchemaApi} from 'amis/lib/Schema'; +import {SchemaApi, SchemaObject} from 'amis/lib/Schema'; export type OptionControlItem = Option & {checked?: boolean, _key?: string}; export interface OptionControlProps extends FormControlProps { className?: string; + otherApiFooter?: Array } export interface OptionControlState { @@ -513,7 +514,7 @@ export default class TreeOptionControl extends React.Component< } renderApiPanel() { - const {render} = this.props; + const {render, otherApiFooter = []} = this.props; const {source, api, labelField, valueField} = this.state; if (source !== 'api') { return null; @@ -547,7 +548,8 @@ export default class TreeOptionControl extends React.Component< value: valueField, placeholder: '值对应的字段', onChange: this.handleValueFieldChange - } + }, + ...otherApiFooter ] }) ); diff --git a/packages/amis-editor/src/validator.tsx b/packages/amis-editor/src/validator.tsx index 08767838c..7991dcecf 100644 --- a/packages/amis-editor/src/validator.tsx +++ b/packages/amis-editor/src/validator.tsx @@ -118,7 +118,8 @@ export enum ValidatorTag { Number = '7', File = '8', Date = '9', - Code = '10' + Code = '10', + Tree = '11' } registerValidator( @@ -133,7 +134,8 @@ registerValidator( [ValidatorTag.Code]: ValidTagMatchType.isDefault, [ValidatorTag.Email]: ValidTagMatchType.isDefault, [ValidatorTag.Password]: ValidTagMatchType.isDefault, - [ValidatorTag.URL]: ValidTagMatchType.isDefault + [ValidatorTag.URL]: ValidTagMatchType.isDefault, + [ValidatorTag.Tree]: ValidTagMatchType.isDefault, } }, {