本文档主要用于对于魔方组件、action、plugin插件二开流程的梳理以及发布。
魔方组件分为两种:
详见 魔方Cli工具使用指南
魔方组件配置包括:样式配置 以及 个性化配置 部分,在 editor.js 或者 组件的 props 目录,前者是旧版,后者是新版。
区分一下旧版和新版,旧版是在 editor.js 下编写一堆对象,新版是 基于magicProps、magicElements、magicElementsProps 去注入配置生成。
元素默认是可以拖拽来调整大小和位置的,如果要禁用该行为,可以添加 data-elemdraggable="false" 属性,如:
<div className="play-btn btn" data-editable="play-btn" data-elemdraggable="false" onClick={this.handleClick} />
旧版基于className和data-editable的元素选中有两个问题 ① 元素渲染的 DOM 层级必须在组件层级下面 ② 最终注入的 css 选择器为 ID + Classname,一些跨端方案如 Lynx 不支持。
说明
新版方案的几个修改:
${props.id}_${elementId}${props.id}_magic_class_${elementId}其中,props.id 即为注入给组件的id, elementId 为组件自定义的唯一id。
新、旧版的配置对比,旧版都是写在 editor.js 里,新版抽取了三个配置设置。
// 旧版 editor.js 配置 const config = { category: 'lottery', name: 'lotteryspin', priorStyleConfig: [ 'MagicLayerMode', { type: 'MagicPosXSize', initValue: { width: '640px', height: '143px', }, }, ], props: [{ cpt_key: 'dataSource', type: 'MagicSelect', uiType: "block", desc: { _MDS_KEY: "dataSource_desc", _MDS_VALUE: "数据来源", }, multiple: true, optionList: [], }], elementsConfig: [elementsConfig], optionalStyleConfig: [optionalStyleConfig], extra: [extra] }
说明
具体配置可见:B端控件editor.js配置手册(对外)
新版
Cli 提供三种设置配置的方式,config 与旧版的属性字段一致。
//panel.ts magicProps('dataSource', { type: 'MagicSelect', uiType: 'block', desc: { _MDS_KEY: 'dataSource_desc', _MDS_VALUE: '抽奖数据来源', }, multiple: true, optionList: [], }); magicElements('address-wrapper', { name: { _MDS_VALUE: '地址填写页'} }) magicElementsProps('address-wrapper', 'addressBgCollapse', { type: 'MagicCollapse', showDivide: true, desc: '背景及标题', initValue: true, }) // index.tsx 引入panel.ts import './panel';
magicElements与magicElementsProps区别:magicElementsProps能直接配置选中元素内部属性,下面两种写法效果一致。
magicElements("title", { name: "标题", optionalStyleConfig: ["MagicFont"], props: [{ cpt_key: "text", type: "MagicInput", desc: "文本" }], }); magicElementsProps("title", "text", { type: "MagicInput", desc: "文本" });
magic-cli 进行项目初始化,组件类型自选,其他常规。
editor.js 配置示例:
module.exports = { category: "other", // 组件类型,上方选择 appId: ["0"], // 0 表示公共业务线 name: "testOldComponent", // 组件名称 // 个性化配置 // hide 字段不传默认是false // props 选项是对象形式 props: [{ cpt_key: 'contentSetting', type: 'MagicCollapse', desc: { _MDS_KEY: 'contentSetting_desc', _MDS_VALUE: '内容设置' }, initValue: true, }, { cpt_key: 'formNamespace', type: 'MagicInput', desc: { _MDS_: '表单名称', }, initValue: '', hide: true, disabled: true, filterRegExp: '\\S+', filterFailDesc: { _MDS_VALUE: '表单名称不能为空', }, }, { cpt_key: 'formItemName', type: 'MagicInput', initValue: '', desc: { _MDS_KEY: 'formItemName_desc', _MDS_VALUE: '表单项名称' }, hide: false, filterFunc: `function (props, dispatch, UI) { if (props.contentSetting === false) { dispatch({ 'hide': true }); } }`, }, ], // 优先的样式配置部分,可自定义选择 // 灵活支持字符串和对象两种配置方式,对象配置,传入该组件需要的参数 priorStyleConfig: [ { type: "MagicWidthXHeight", initValue: { width: '300px', height: '100px' } }, ], // 底部允许的样式配置部分,可选 // MagicBorder, MagicFont, MagicBackgroundImage, MagicBackgroundColor, MagicBackground, MagicOpacity optionalStyleConfig: [ "MagicBackground", "MagicOpacity" ], // 元素配置 // 显示说明可配置元素会被哪些配置影响,不指定则选中元素时不展示配置 elementsConfig: [{ className: "usename", // 类名,不带.号 name: { _MDS_: "用户昵称"}, // B端右侧配置区最上方的展示title optionalStyleConfig: [], // 受影响的配置项的cpt_key集合 effectCptKeys: ["formItemName"], props: [ { cpt_key: 'name', type: 'MagicInput', desc: { _MDS_: '用户名称', }, initValue: '', } ], // 元素可配置的样式 priorStyleConfig: [ 'MagicWidthXHeight', { type: 'MagicLeftXTop', }, 'MagicOpacity', { type: 'MagicTRBL', styleName: 'border-radius', }, 'MagicDivide', ], }], desc: { "_MDS_KEY": "desc", "_MDS_VALUE": "测试旧版" }, // 描述内容,左侧组件卡片展示出来的名称 introduction: { "_MDS_KEY": "introduction", "_MDS_VALUE": "测试旧版组件介绍" // 填入组件介绍 }, preview:"../assets/logo.png", //组件预览图路径相对地址 thumbnail: "", // 组件icon路径相对地址 thumbnailType: "icon", // 组件类型:image/icon tutorial: "", //教程地址,填写doc链接即可 abilities: {}, // 组件能力,可以定义组件的能力,可见 附录 extra: { // 配置一些额外能力 "action": false, // 配置事件动作能力,需要的传入对象 "api": false, // api配置信息 "tea": [ {event: "TEATEST", eventDesc: { _MDS_VALUE : "tea打点描述,参数有inappEvent:Test" }}], // 挂载tea上报事件 } }
index.js / index.jsx上修改
import React from 'react'; import './index.scss'; const TestOldComponent = (props) => { return ( <div id={props.id} className="mtestOldComponent" data-comp-type={props.magicComType} > {/* elementsConfig 设置选中元素的classname需要 对应注入 */} <div className='usename' data-editable="usename"> {props.usename_name || '用户名称'} </div> <input /> </div> ); } export default TestOldComponent;
本地开发调试 & 上线
// 选择你所需要的地址 ~ mgx region use xxx // 本地开放 ~ mgx dev
打开编辑器页面,加入?debug=1,找到本地开发的组件。
调试完成之后上线。
// 上传 ~ mgx bundle // 发布 ~ mgx deploy
说明
以上代码可以直接copy进cli init后的对应文件,进行本地调试。
目前对外不支持新版开发,原因是能力引用了内部模块内容,待后续有需要看如何开放兼容。
类型值 | 类型名称 |
|---|---|
common | 基础 |
button | 按钮类 |
form | 表单类 |
container | 容器类 |
tab | 选项卡 |
lottery | 抽奖类 |
vote | 投票类 |
question | 答题类 |
audiovideo | 音视频 |
list | 列表类 |
poi | poi类 |
animation | 动画效果 |
other | 其他 |
develop | 本地开发 |
可详见这里,找出自定义想要的配置方法 B端控件editor.js配置手册(对外) 。
装完magic-cli后,进行项目初始化。
进入目录,修改./src/index.tsx,根据自身需要实现的功能编写代码。
~ cd testeditor
写入一些自定义的逻辑,如下测试用例:功能是 根据输入框的值,会在下方进行输出。
import React, { useState, useMemo } from 'react'; import MagicInput from '@editorcomponent/MagicInput'; import './index.scss'; const Testeditor = (props) => { const { compValue, desc, key, onChange } = props; return ( <div className="editorcomponent-testeditor"> {desc}: <MagicInput compValue={compValue} onChange={(val: string) => { onChange(val); }} /> {compValue && `输入内容:${compValue}`} </div> ); } export default Testeditor;
进入需要使用的组件目录,修改editor.js/ts 中的 props 。
如在 select 组件增加一个配置,其中 type 值可见 testeditor 目录下的 h5(或者是pc)/configs/base.json 的 uniqueId 字段。
本地开放时,在使用组件 【select】组件目录 和 自定义组件【testeditor】目录下,启动 socket 服务。
在自定义右侧配置组件目录,即【testeditor】和 使用的组件目录【select】目录下,启动socket服务。
// 切换到 testeditor目录终端 ~ cd testeditor/h5 ~ mgx dev // 切换到 select 目录终端 ~ cd select/h5 ~ mgx dev
testeditor:dev打包
select:dev打包
打开编辑器网页,开启 Socket 监听,进入开发者模式。
测试一下功能:底下会打印输出的内容,测试成功。
进行功能开发&调试,比如我需要在原先下方输出值的同时,再加一个计算含有数字总值的功能。
import React, { useState, useMemo } from 'react'; import MagicInput from '@editorcomponent/MagicInput'; import './index.scss'; const Testeditor = (props) => { const { compValue, desc, key, onChange } = props; const [numList, setNumList] = useState([]); const total = useMemo(() => { return numList.reduce((prev, next) => { return prev + Number(next) }, 0) }, [numList]) return ( <div className="editorcomponent-testeditor"> {desc}: <MagicInput compValue={compValue} onChange={(val: string) => { setNumList(val.match(/\d/g) || []); onChange(val); }} /> {compValue && `输入内容:${compValue}`} {total > 0 && `含有数字总值为:${total}`} </div> ); } export default Testeditor;
效果如下:
本地开发调试完毕之后,保存代码,关掉socket连接,确认无误之后,发布上线。
~ mgx upload ~ mgx publish
react |
react-dom |
react-redux |
@ies/semi-ui-react |
@editorcomponent/MagicInput |
@editorcomponent/MagicSelect |
@editorcomponent/....... |
@editor/utils |
action动作是一个方法的声明,包括方法的类型、名称、参数以及方法代码。
动作概念,是以配置的方式,将事件(click、change)与动作关联起来,即可以以配置的方式实现页面交互。
mgx init ,初始化名为 testaction 的动作,选择 action 类型。testaction/h5 ├── configs │ ├── base.json │ └── editor.js ├── dist // 初始化的时候没有 ├── entry // 初始化的时候没有 ├── package.json └── src └── index.js
// 上传 ~ mgx bundle // 发布 ~ mgx deploy
完成上传后用到action动作的组件会看到新动作插入。
比如要开发一个点击跳转到其他网页的功能。
先编写配置文件:
// editor.js module.exports = { // appId: ['0'], // 提供给业务线使用,可选 actionType: 'custom', // 动作类型 actionName: 'testaction', // 动作名 actionDesc: { // 动作描述 _MDS_KEY: 'desc', _MDS_VALUE: '测试动作', }, actionParams: [ // action组件的参数对象, { cpt_key: 'locationHref', desc: { _MDS_VALUE: '网址', }, type: 'MagicInput', initValue: { _MDS_VALUE: '', }, width: '100%', }, ], env: ['in', 'out', 'all'], // 执行环境 in 端内 out 端外 all 全部 hide: false, // 编辑器事件动作配置面板中显示、隐藏 };
代码实现:index.js
function (actionParams,node,extra,args) { location.href = actionParams.locationHref; }
参数说明:
参数名 | 类型 | 详情 |
|---|---|---|
actionParams | object | 编辑器事件动作配置面板,手动配置的参数,静态参数 |
node | object | schema 节点 |
extra | object |
|
args | object | 运行时传入的动态参数 |
使用组件必须在自己的配置文件 editor.js 添加配置项 extra.action,如在 “矩形形状” 组件加入 action 。
action: { filterEvent: ['onClick', 'componentDidMount'], initValue: [ { options: { env: 'all' }, eventType: 'onClick', eventStop: 'return window.__EDITOR__ === true;', hide: true, }, ], },
打开事件动作弹窗,能看到 自定义的 action -- testaction,触发条件有 点击(onClick) 和 加载(componentDidMount) 的时候。
extra.action 配置
extra: { // 值为true时开启将动作代码注入到组件props中的功能, 值为对象时,精确控制可配置的动作类型、参数 action: true ,// 开启后会将动作配置注入到组件props中 action: { // 精确配置 visible:true, // 在编辑器显示动作控制面板,默认为true filterEvent:['onClick', 'onDoubleClick'], //支持配置的事件,默认所有. // 目前魔方支持onClick onDoubleClick, // componentDidMount ,componentWillUnmount onAnimationEnd filterType:['jsb', 'goto'], // 默认所有,与动作的actionType对应支持可配置的动作类型 tool, jsb,go'to //魔方编辑器会将配置的动作代码以eventName名字传入 // 比如当配置了onClick 这个event对应的action为openLiveRoom , 会将openLiveRoom // 对应的代码片段绑定为onClick 作为组件的props传入 initValue: [ // 编辑器动作面板配置的初始值,会依次将对应参数注入到组件props中 , // 当存在多个同名事件时,调用一次, 都会执行。 eventType: "onAnimationEnd", // 事件名 eventStop: "return !!window.__EDITOR__" // 不注入到组件props中的场景。代码片段中this指向魔方提供的公用tool options: { env: "all" , // 端内外场景 'in'|'out'|'all' }, actionType: "state", // 动作类型 'magicUnifyJsb' 'custom' ... actionName: "resetSchemaByNodeId", // 对应的动作名称 actionParams: { // 对应动作的参数 componentId: -1, callback: ` function (props) { return { playing: false }; } `, hide:false, // 配置该默认值项在编辑器是否可见。 }, ] } }
actionType | 说明 | 详情 |
|---|---|---|
magicUnifyJsb | 魔方统一端能力 | |
custom | 自定义动作 |
务线接入时,会填写业务线对应jsb sdk 的cdn地址和挂载到window对象上的jsb对象名称。魔方在运行时会以script 标签的形式加载该js文件,并将该jsb对象挂载到window.__MAGIC__.action.jsb 这一对象上。业务线在调用端能力时可直接使用该方法。
插件为业务线的C端页面提供了可插拔的代码植入方式。
通过插件,研发人员可以在页面不同位置植入代码,去执行一些全局定制逻辑。甚至可以替换魔方提供的页面渲染引擎。
在本地的文件夹中执行 mgx init 进行初始化,选择 plugin 类型。
配置文件
// editor.js module.exports = { appId: ["0"], desc: { "_MDS_KEY": "desc", "_MDS_VALUE": "背景颜色" // 插件名称 }, introduction: '改变背景颜色', // 插件功能描述 position: "bodyBottom", // 插件插入页面位置 headTop、headBottom,bodyBottom, bundle类型可以注释掉 online: true, pluginType: "ejs", // ejs模板,或者是bundle打包 panelType: 'smartWindow', // 参数填写弹窗类型,目前仅支持 smartWindow // 启用插件时,可填写的的参数 pluginParams: [ { cpt_key: 'color', type:'MagicInput', initValue:'#ffffff', desc: { _MDS_VALUE: '背景颜色' }, placeholder: '请输入' } ], }
插件文件
<!-- index.ejs --> <!-- 编写插件内容 --> <script> // 可通过 ejs 的变量语法,获取启用插件时填写的参数,例如:editor.js 中配置了 color 参数,这里通过 <%= color %> 获取 document.body.style.backgroundColor = '<%= color %>' </script>
pluginType
自定义开发的组件,需要经过注册,编译,结合 proxy 代理到页面上。
cd /input,右图是cd exchange01/h5。mgx -v mgx register mgx dev
如果需要引入lib中的工具库,如tio tool,需要在package.json中添加,然后pnpm install 。
"devDependencies": { "@magic-module/common-lib-bus": "^1.0.2", "@magic-module/common-lib-tio": "^1.0.1", "@magic-module/common-lib-tool": "^1.0.6-beta.0" },
然后在src同级增加目录typings,里面新增index.d.ts,并声明模块
declare module '@magic-module/common-lib-tio' declare module '@magic-module/common-lib-tool'
插件的开启状态、配置的属性,可在组件中获取,支持在 2 个场景中获取。
在 editor.js 中添加提个隐藏属性,使用 store 获取活动信息,从中获取插件信息。
{ // ... props: [ // ... { cpt_key: 'plugin_props_a', hidden: true, filterFunc: function (props, dispatch) { // 1. 需要获取数据的插件名称 const name = '@magic-module/common-plugin-bodycolor' // 2. 从 store 中获取插件,插件开启后才能获取到 const plugin = store.getState().config?.activityData?.plugins?.default?.find(item => item.name === name) // 3. 获取属性值,比如属性名为 pageNo const value = plugin?.params.pageNo // 4. 将属性值设置到 plugin_props_a 的值中 // 组件中通过 this.props.plugin_props_a 获取 if (value) { dispatch({ initValue: value }) } } } ] // ... }
在组件首次添加、再次选中时,将执行 filterFunc 进行获取。
所以,如果开启插件后,没有再选中组件,将无法获取。如果是 C 端场景,请使用 C 端获取方式稳定获取。
通过全局变量获取,获取前需要判断是否在编辑器内,编辑器内无法获取。
if (!tool.isEditor()) { // 1. 需要获取数据的插件名称 const name = '@magic-module/common-plugin-bodycolor' // 2. 获取属性值,比如属性名为 pageNo const value = window.__MAGIC__.plugins[name].params.pageNo }