diff --git a/packages/amis-editor/src/plugin/CRUD.tsx b/packages/amis-editor/src/plugin/CRUD.tsx index 1ee50b729..98f799f82 100644 --- a/packages/amis-editor/src/plugin/CRUD.tsx +++ b/packages/amis-editor/src/plugin/CRUD.tsx @@ -188,7 +188,7 @@ export class CRUDPlugin extends BasePlugin { scaffoldForm: ScaffoldForm = { title: '增删改查快速开始-CRUD', body: [ - getSchemaTpl('api', { + getSchemaTpl('apiControl', { label: '接口地址', sampleBuilder: (schema: any) => JSON.stringify( diff --git a/packages/amis-editor/src/plugin/Form/Form.tsx b/packages/amis-editor/src/plugin/Form/Form.tsx index 7c6b2c2b5..4a699a522 100644 --- a/packages/amis-editor/src/plugin/Form/Form.tsx +++ b/packages/amis-editor/src/plugin/Form/Form.tsx @@ -138,7 +138,7 @@ export class FormPlugin extends BasePlugin { scaffoldForm: ScaffoldForm = { title: '快速创建表单', body: [ - getSchemaTpl('api', { + getSchemaTpl('apiControl', { label: '提交地址' }), { @@ -534,7 +534,7 @@ export class FormPlugin extends BasePlugin { : { title: '接口', body: [ - getSchemaTpl('api', { + getSchemaTpl('apiControl', { label: '保存接口', description: '用来保存表单数据', sampleBuilder: () => `{ @@ -563,7 +563,7 @@ export class FormPlugin extends BasePlugin { pipeOut: (value: any) => (value ? '' : undefined) }), - getSchemaTpl('api', { + getSchemaTpl('apiControl', { name: 'asyncApi', label: '异步检测接口', visibleOn: 'data.asyncApi != null', @@ -575,7 +575,7 @@ export class FormPlugin extends BasePlugin { type: 'divider' }, - getSchemaTpl('api', { + getSchemaTpl('apiControl', { name: 'initApi', label: '初始化接口', description: '用来初始化表单数据', @@ -657,7 +657,7 @@ export class FormPlugin extends BasePlugin { pipeOut: (value: any) => (value ? '' : undefined) }), - getSchemaTpl('api', { + getSchemaTpl('apiControl', { name: 'initAsyncApi', label: '异步检测接口', visibleOn: 'data.initAsyncApi != null', diff --git a/packages/amis-editor/src/plugin/Page.tsx b/packages/amis-editor/src/plugin/Page.tsx index 8a9441258..228f6bbd6 100644 --- a/packages/amis-editor/src/plugin/Page.tsx +++ b/packages/amis-editor/src/plugin/Page.tsx @@ -170,21 +170,36 @@ export class PagePlugin extends BasePlugin { { title: '接口', body: [ - getSchemaTpl('api', { + getSchemaTpl('apiControl', { label: '数据初始化接口', name: 'initApi', sampleBuilder: () => `{ - "status": 0, - "msg": "", - - data: { - // 示例数据 - "id": 1, - "a": "sample" - } -}` + "status": 0, + "msg": "", + + data: { + // 示例数据 + "id": 1, + "a": "sample" + } + }` }), + // getSchemaTpl('api', { + // label: '数据初始化接口', + // name: 'initApi', + // sampleBuilder: () => `{ + // "status": 0, + // "msg": "", + + // data: { + // // 示例数据 + // "id": 1, + // "a": "sample" + // } + // }` + // }), + getSchemaTpl('initFetch'), getSchemaTpl('switch', { diff --git a/packages/amis-editor/src/renderer/APIControl.tsx b/packages/amis-editor/src/renderer/APIControl.tsx index 90cc9ac7e..e334424f2 100644 --- a/packages/amis-editor/src/renderer/APIControl.tsx +++ b/packages/amis-editor/src/renderer/APIControl.tsx @@ -237,24 +237,43 @@ export default class APIControl extends React.Component< onPickerClose?.(); } + @autobind renderHeader() { - const {render, actions, enablePickerMode} = this.props; + const {render, label, labelRemark, useMobileUI, popOverContainer, env} = + this.props; + const classPrefix = env?.theme?.classPrefix; - const actionsDom = - Array.isArray(actions) && actions.length > 0 - ? actions.map((action, index) => { - return render(`action/${index}`, action, { - key: index, - onAction: this.handleAction.bind(this, action) - }); - }) - : null; + // const actionsDom = + // Array.isArray(actions) && actions.length > 0 + // ? actions.map((action, index) => { + // return render(`action/${index}`, action, { + // key: index, + // onAction: this.handleAction.bind(this, action) + // }); + // }) + // : null; - return actionsDom || enablePickerMode ? ( -
- {enablePickerMode ? this.renderPickerSchema() : actionsDom} + return ( +
+
- ) : null; + ); } renderPickerSchema() { @@ -953,6 +972,7 @@ export default class APIControl extends React.Component< } @FormItem({ - type: 'ae-apiControl' + type: 'ae-apiControl', + renderLabel: false }) export class APIControlRenderer extends APIControl {} diff --git a/packages/amis-editor/src/renderer/OptionControl.tsx b/packages/amis-editor/src/renderer/OptionControl.tsx index 1f9caf56b..589db8f9a 100644 --- a/packages/amis-editor/src/renderer/OptionControl.tsx +++ b/packages/amis-editor/src/renderer/OptionControl.tsx @@ -47,7 +47,7 @@ export interface OptionControlState { api: SchemaApi; labelField: string; valueField: string; - source: 'custom' | 'api' | 'form'; + source: 'custom' | 'api' | 'apicenter'; } export default class OptionControl extends React.Component< @@ -209,7 +209,7 @@ export default class OptionControl extends React.Component< data.value = defaultValue || undefined; } - if (source === 'api') { + if (source === 'api' || source === 'apicenter') { const {api, labelField, valueField} = this.state; data.source = api; data.labelField = labelField; @@ -296,7 +296,7 @@ export default class OptionControl extends React.Component< * 切换选项类型 */ @autobind - handleSourceChange(source: 'custom' | 'api' | 'form') { + handleSourceChange(source: 'custom' | 'api' | 'apicenter') { this.setState({source: source}, this.onChange); } @@ -396,8 +396,15 @@ export default class OptionControl extends React.Component< } renderHeader() { - const {render, label, labelRemark, useMobileUI, env, popOverContainer} = - this.props; + const { + render, + label, + labelRemark, + useMobileUI, + env, + popOverContainer, + hasApiCenter + } = this.props; const classPrefix = env?.theme?.classPrefix; const {source} = this.state; const optionSourceList = ( @@ -407,16 +414,17 @@ export default class OptionControl extends React.Component< value: 'custom' }, { - label: '接口获取', + label: '外部接口', value: 'api' - } + }, + ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []) // { // label: '表单实体', // value: 'form' // } ] as Array<{ label: string; - value: 'custom' | 'api' | 'form'; + value: 'custom' | 'api' | 'apicenter'; }> ).map(item => ({ ...item, @@ -698,7 +706,7 @@ export default class OptionControl extends React.Component< renderApiPanel() { const {render} = this.props; const {source, api, labelField, valueField} = this.state; - if (source !== 'api') { + if (source === 'custom') { return null; } @@ -711,6 +719,7 @@ export default class OptionControl extends React.Component< visibleOn: 'data.autoComplete !== false', value: api, onChange: this.handleAPIChange, + sourceType: source, footer: [ { label: tipedLabel( diff --git a/packages/amis-editor/src/renderer/TreeOptionControl.tsx b/packages/amis-editor/src/renderer/TreeOptionControl.tsx index 8989dcb8e..59f0b105a 100644 --- a/packages/amis-editor/src/renderer/TreeOptionControl.tsx +++ b/packages/amis-editor/src/renderer/TreeOptionControl.tsx @@ -26,7 +26,7 @@ import type {Option} from 'amis'; import type {FormControlProps} from 'amis-core'; import {SchemaApi} from 'amis/lib/Schema'; -export type OptionControlItem = Option & {checked?: boolean, _key?: string}; +export type OptionControlItem = Option & {checked?: boolean; _key?: string}; export interface OptionControlProps extends FormControlProps { className?: string; @@ -37,14 +37,14 @@ export interface OptionControlState { api: SchemaApi; labelField: string; valueField: string; - source: 'custom' | 'api'; - modalVisible: boolean + source: 'custom' | 'api' | 'apicenter'; + modalVisible: boolean; } const defaultOption: OptionControlItem = { label: '', value: '' -} +}; export default class TreeOptionControl extends React.Component< OptionControlProps, @@ -69,9 +69,11 @@ export default class TreeOptionControl extends React.Component< } transformOptions(props: OptionControlProps) { - const {data: {options}} = props; + const { + data: {options} + } = props; if (!options || !options.length) { - return [{...defaultOption}] + return [{...defaultOption}]; } return options; } @@ -87,7 +89,10 @@ export default class TreeOptionControl extends React.Component< if (option.children && option.children.length) { option.children = this.pretreatOptions(option.children); } - option.value = option.value == null || option.value === '' ? option.label : option.value; + option.value = + option.value == null || option.value === '' + ? option.label + : option.value; return option; }); } @@ -109,7 +114,7 @@ export default class TreeOptionControl extends React.Component< data.options = this.pretreatOptions(options); } - if (source === 'api') { + if (source === 'api' || source === 'apicenter') { const {api, labelField, valueField} = this.state; data.source = api; data.labelField = labelField; @@ -123,7 +128,7 @@ export default class TreeOptionControl extends React.Component< * 切换选项类型 */ @autobind - handleSourceChange(source: 'custom' | 'api') { + handleSourceChange(source: 'custom' | 'api' | 'apicenter') { this.setState({source: source}, this.onChange); } @@ -134,23 +139,27 @@ export default class TreeOptionControl extends React.Component< labelRemark, useMobileUI, env, - popOverContainer + popOverContainer, + hasApiCenter } = this.props; const classPrefix = env?.theme?.classPrefix; const {source} = this.state; - const optionSourceList = ([ - { - label: '自定义选项', - value: 'custom' - }, - { - label: '接口获取', - value: 'api' - } - ] as Array<{ - label: string; - value: 'custom' | 'api'; - }>).map(item => ({ + const optionSourceList = ( + [ + { + label: '自定义选项', + value: 'custom' + }, + { + label: '外部接口', + value: 'api' + }, + ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []) + ] as Array<{ + label: string; + value: 'custom' | 'api' | 'apicenter'; + }> + ).map(item => ({ ...item, onClick: () => this.handleSourceChange(item.value) })); @@ -200,11 +209,11 @@ export default class TreeOptionControl extends React.Component< ); } - handleEditLabelOrValue (value: string, path: string, key: string) { - const options = cloneDeep(this.state.options); - const {path: nodePath} = this.getNodePath(path); - set(options, `${nodePath}.${key}`, value); - this.setState({options}, () => this.rereshBindDrag()); + handleEditLabelOrValue(value: string, path: string, key: string) { + const options = cloneDeep(this.state.options); + const {path: nodePath} = this.getNodePath(path); + set(options, `${nodePath}.${key}`, value); + this.setState({options}, () => this.rereshBindDrag()); } @autobind handleDelete(pathStr: string, index: number) { @@ -216,12 +225,12 @@ export default class TreeOptionControl extends React.Component< const path = pathStr.split('-'); if (path.length === 1) { options.splice(index, 1); - } - else { + } else { const {parentPath} = this.getNodePath(pathStr); const parentNode = get(options, parentPath, {}); parentNode?.children?.splice(index, 1); - if (!parentNode?.children.length) { // 去除僵尸子节点 + if (!parentNode?.children.length) { + // 去除僵尸子节点 delete parentNode.children; } set(options, parentPath, parentNode); @@ -231,7 +240,7 @@ export default class TreeOptionControl extends React.Component< @autobind getNodePath(pathStr: string) { let pathArr = pathStr.split('-'); - if(pathArr.length === 1) { + if (pathArr.length === 1) { return { path: pathArr, parentPath: '' @@ -251,8 +260,7 @@ export default class TreeOptionControl extends React.Component< const path = pathStr.split('-'); if (path.length === 1) { options.splice(+path[0] + 1, 0, {...defaultOption}); // 加在后面一项 - } - else { + } else { const index = path[path.length - 1]; const {parentPath} = this.getNodePath(pathStr); const parentNode = get(options, parentPath, {}); @@ -264,7 +272,9 @@ export default class TreeOptionControl extends React.Component< @autobind addChildOption(pathStr: string) { if (pathStr.split('-').length >= 7) { - toast.warning('层级过深,建议使用【接口获取】管理选项', {closeButton: true}); + toast.warning('层级过深,建议使用【接口获取】管理选项', { + closeButton: true + }); return; } const options = cloneDeep(this.state.options); @@ -272,8 +282,7 @@ export default class TreeOptionControl extends React.Component< const node = get(options, path) || []; if (node.children) { node.children.push({...defaultOption}); - } - else { + } else { node.children = [{...defaultOption}]; } set(options, path, node); @@ -292,22 +301,26 @@ export default class TreeOptionControl extends React.Component< if (option.children && option.children.length) { const parent = cloneDeep(option); delete parent.children; - return
- {this.renderOptions(parent, key, indexes)} + return (
- { - option.children.map((option: any, key: number) => { - return this.renderOptions(option, key, indexes.concat(key)) - }) - } + {this.renderOptions(parent, key, indexes)} +
+ {option.children.map((option: any, key: number) => { + return this.renderOptions(option, key, indexes.concat(key)); + })} +
-
+ ); } - return
{ // 这里使用onBlur替代onChange 减少渲染次数 + onBlur={(event: any) => { + // 这里使用onBlur替代onChange 减少渲染次数 this.handleEditLabelOrValue(event.target.value, path, 'label'); }} /> @@ -372,7 +386,8 @@ export default class TreeOptionControl extends React.Component<
- + + ); } @autobind dragRef(ref: any) { @@ -390,28 +405,30 @@ export default class TreeOptionControl extends React.Component< } } initDragging() { - const rootSortable = new Sortable( - this.drag as HTMLElement, - { - group: 'TreeOptionControlGroup', - animation: 150, - handle: '.ae-TreeOptionControlItem-dragBar', - ghostClass: 'ae-TreeOptionControlItem-dragging', - onEnd: (e: any) => { - const options = cloneDeep(this.state.options); - const {oldIndex, newIndex} = e; - [options[newIndex], options[oldIndex]] = [options[oldIndex], options[newIndex]]; - this.setState({options}, () => this.rereshBindDrag()); - }, - onMove: (e: any) => { - const {from, to} = e; - // 暂时不支持跨级拖拽 - return from.dataset.level === to.dataset.level; - } + const rootSortable = new Sortable(this.drag as HTMLElement, { + group: 'TreeOptionControlGroup', + animation: 150, + handle: '.ae-TreeOptionControlItem-dragBar', + ghostClass: 'ae-TreeOptionControlItem-dragging', + onEnd: (e: any) => { + const options = cloneDeep(this.state.options); + const {oldIndex, newIndex} = e; + [options[newIndex], options[oldIndex]] = [ + options[oldIndex], + options[newIndex] + ]; + this.setState({options}, () => this.rereshBindDrag()); + }, + onMove: (e: any) => { + const {from, to} = e; + // 暂时不支持跨级拖拽 + return from.dataset.level === to.dataset.level; } - ); + }); this.sortables.push(rootSortable); - const parents = this.drag?.querySelectorAll('.ae-TreeOptionControlItem-son'); + const parents = this.drag?.querySelectorAll( + '.ae-TreeOptionControlItem-son' + ); if (!parents) { return; } @@ -432,7 +449,10 @@ export default class TreeOptionControl extends React.Component< const {parentPath} = this.getNodePath(nodePath); const children = get(options, `${parentPath}.children`) || []; if (children) { - [children[oldIndex], children[newIndex]] = [children[newIndex], children[oldIndex]]; + [children[oldIndex], children[newIndex]] = [ + children[newIndex], + children[oldIndex] + ]; set(options, `${parentPath}.children`, children); this.setState({options}); } @@ -467,32 +487,38 @@ export default class TreeOptionControl extends React.Component< this.hideModal(); }} > - { + { + this.hideModal(); + }} + > + 选项管理 + + +
+ {options.map((option, key) => + this.renderOptions(option, key, [key]) + )} +
+
+ + - - + 取消 + + + ); } @@ -515,7 +541,7 @@ export default class TreeOptionControl extends React.Component< renderApiPanel() { const {render} = this.props; const {source, api, labelField, valueField} = this.state; - if (source !== 'api') { + if (source === 'custom') { return null; } @@ -528,6 +554,7 @@ export default class TreeOptionControl extends React.Component< visibleOn: 'data.autoComplete !== false', value: api, onChange: this.handleAPIChange, + sourceType: source, footer: [ { label: tipedLabel( @@ -569,9 +596,11 @@ export default class TreeOptionControl extends React.Component< onClick={() => { this.setState({ modalVisible: true - }) + }); }} - >选项管理 + > + 选项管理 + {this.renderModal()}