自定义配置项开发
如当前 表单类、数据源类 配置项无法满足自定义组件的特殊属性配置交互(例如:特殊的数值格式、特殊的取值交互方式等),支持在自定义组件工程中 开发自定义配置项,并通过 amis 的 FormItem 注册为新的配置项类型 type,再在组件模型文件(model.ts)的属性配置(propsSchema) 中添加使用。
如需了解平台内置的配置项,请参阅 表单类配置项 和 数据源类配置项。
整体流程:开发自定义配置项组件 → 使用 @FormItem 注册并导出 → 在组件模型文件中 import 此自定义配置项组件,并在 propsSchema 中添加这个新增的配置项类型。
步骤一:开发自定义配置项组件
在 自定义组件项目(非 src/components 直接子目录,避免识别成自定义组件)中开发自定义配置项组件:
- 展示只读摘要或占位输入,以及打开弹窗、抽屉等复杂编辑区;
- 在确认保存时,将结构化结果通过
onChange写回表单; - 需要读取页面/表单上下文时,可通过
RendererProps(如data、disabled)获取。
完整可参考示例工程中的实现(弹窗 + JSON Schema 编辑器):
targetNumber__c/customStyleConfig/index.tsx(GitHub)
示例中 propsSchema 里传入的 viewStyle、wideScreen 等字段,会作为普通 props 进入该组件,可按需扩展。
步骤二:使用 FormItem 注册为 amis 表单项并导出
使用 amis 提供的 FormItem 装饰器(或等价 API)将组件注册为表单项,type 字段必须与后续 propsSchema 中的 type 完全一致。
tsx
// @ts-ignore
import { FormItem, RendererProps } from 'amis';
import React from 'react';
import { Input, Button, Modal, message } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
// @ts-ignore
import JSONEditor from '@wibetter/json-editor';
import '@wibetter/json-editor/lib/index.css';
import { configSchema } from './configSchema';
import './index.scss';
interface ICustomStyleConfigProps {
onChange?: (value: unknown) => void;
value?: unknown;
name: string;
/** 可由 propsSchema 传入的扩展配置 */
viewStyle?: string;
wideScreen?: boolean;
}
interface ICustomStyleConfigState {
isModalVisible: boolean;
editorValue: unknown;
}
@FormItem({
type: 'customStyleConfig',
/** 当列出的 props 变化时需要重渲染时在此声明,例如依赖表单 data */
detectProps: ['data'],
})
export default class CustomStyleConfigRenderer extends React.Component<
RendererProps & ICustomStyleConfigProps,
ICustomStyleConfigState
> {
constructor(props: RendererProps & ICustomStyleConfigProps) {
super(props);
this.state = {
isModalVisible: false,
editorValue: props.value ?? {},
};
this.showModal = this.showModal.bind(this);
this.handleModalOk = this.handleModalOk.bind(this);
this.handleModalCancel = this.handleModalCancel.bind(this);
this.handleEditorChange = this.handleEditorChange.bind(this);
this.getDisplayValue = this.getDisplayValue.bind(this);
}
handleEditorChange(value: unknown) {
this.setState({ editorValue: value });
}
handleModalOk() {
const { editorValue } = this.state;
const { onChange } = this.props;
try {
onChange?.(editorValue);
this.setState({ isModalVisible: false });
} catch {
message.error('保存配置失败');
}
}
handleModalCancel() {
const value = this.props.value ?? {};
this.setState({ isModalVisible: false, editorValue: value });
}
showModal() {
const value = this.props.value ?? {};
this.setState({ isModalVisible: true, editorValue: value });
}
getDisplayValue() {
const { value } = this.props;
if (!value || typeof value !== 'object' || Object.keys(value as object).length === 0) {
return '';
}
return '已配置自定义样式';
}
render() {
const { disabled, viewStyle, wideScreen, value } = this.props;
const { isModalVisible } = this.state;
return (
<div className="properties-panel-custom-style-config">
<div className="custom-style-config-input-container">
<Input
value={this.getDisplayValue()}
placeholder="请配置自定义样式"
disabled={disabled}
readOnly
className="custom-style-config-input"
/>
<Button
type="text"
icon={<SettingOutlined />}
onClick={this.showModal}
disabled={disabled}
className="custom-style-config-setting-btn"
/>
</div>
<Modal
title="自定义样式配置"
visible={isModalVisible}
onOk={this.handleModalOk}
onCancel={this.handleModalCancel}
width={800}
centered
okText="确定"
cancelText="取消"
destroyOnClose={false}
>
<div className="custom-style-config-modal-content">
{isModalVisible && (
<JSONEditor
schemaData={configSchema}
jsonData={value}
onChange={this.handleEditorChange}
viewStyle={viewStyle || 'tabs'}
tabPosition="left"
wideScreen={wideScreen ?? false}
/>
)}
</div>
</Modal>
</div>
);
}
}与属性面板的约定
| 约定 | 说明 |
|---|---|
| 当前值 | 通过 this.props.value 读取(与 defaultComProps 中同名字段的初始值对应)。 |
| 写回表单 | 用户确认配置后调用 this.props.onChange(newValue),否则设计器无法持久化。 |
detectProps | 若渲染依赖 data 等上下文字段变化,需在 @FormItem 中声明,否则可能不会更新。 |
| 标签与布局 | label、description、校验提示、表单项布局等由 amis 表单统一处理,无需在自定义组件内重复实现。 |
更多扩展方式见 amis 官方文档:自定义 React — 表单项扩展。
步骤三:在组件模型文件 / 属性配置(propsSchema)中添加使用
- 引入注册侧产物:确保包含
@FormItem注册的模块被执行一次(通常通过import '.../customStyleConfig'或包导出的 side-effect 入口),样式文件按需单独引入。 propsSchema中声明:type与@FormItem的type相同,name与defaultComProps中的属性键一致,保证画布默认值与面板编辑的是同一字段。
ts
// 侧效 import:注册 amis 表单项(路径以实际构建产物为准)
import 'neo-bi-cmps/lib/customStyleConfig';
import 'neo-bi-cmps/lib/customStyleConfig.css';
export class TargetNumberModel {
isAmisCmp: boolean = true;
label: string = '数值指标';
description: string =
'用于展示关键数值指标,支持从 XObject 实体对象获取动态数据,支持绑定多个字段进行展示';
tags: string[] = ['业务组件'];
iconUrl: string = 'https://custom-widgets.bj.bcebos.com/TargetNumber.svg';
targetPage: string[] = ['all'];
targetDevice: string = 'all';
defaultComProps = {
entityApiKey: '',
targetNumberStyle: {
baseStyle: {
backgroundColor: '#ffffff',
paddingMargin: { margin: '0', padding: '0' },
},
layoutStyle: { alignClass: 'flex-col' },
titleStyle: {
show: false,
text: '',
fontSize: 24,
fontWeight: 400,
color: '#000',
},
numberStyle: {
fontSize: 32,
fontWeight: 600,
color: '#262626',
},
numberTitleStyle: {
fontSize: 14,
fontWeight: 400,
color: '#8c8c8c',
},
},
};
functions = [
{
apiKey: 'loadData',
label: '刷新数据',
helpTextKey: '重新加载数值指标数据',
},
];
propsSchema = [
{
type: 'xObjectDetailApi',
name: 'entityApiKey',
label: '绑定实体业务数据',
placeholder: '绑定实体业务数据源',
disabledFieldSelect: true,
disabledAutoFetchData: true,
},
{
type: 'selectFieldDescApi',
name: 'selectFieldDesc',
xObjectApiKey: 'entityApiKey.xObjectApiKey',
mode: 'tags',
label: '绑定字段',
placeholder: '请至少选择一个要显示的字段',
},
{
type: 'customStyleConfig',
name: 'targetNumberStyle',
label: '自定义样式配置',
viewStyle: 'tabs',
wideScreen: false,
},
];
}
export default TargetNumberModel;说明
import的入口路径需与项目打包配置一致(示例为neo-bi-cmps发布包路径;单体仓库可为相对路径指向注册文件)。- 自定义
type名称在全局需唯一,避免与平台或其他组件重复。 - 若业务使用 antd 5+,Modal 等组件 API 可能与 antd 4(如
visible与open)不同,需按实际版本调整。
