E10 前端二次开发指南

发布于 2026-08-05

相关资料

表单前端对接:泛微在线文档

表单引擎对接文档:e-cology.com.cn/sp/form/components/FormSwitch

ecode 开发

E-Code 平台介绍

E-code 平台的功能和 E9 的 E-Code 平台差不多,它是一个在线的前端代码编辑器,可编写 React 组件以及注册事件,可实现修改标准页面的组件和数据,以及创建自定义页面、进行接口拦截等。

E-Code 页面进入链接: `/ecode`

进入页面后是一个项目列表,你可以在这里创建项目

进入项目后就可以在此页面创建 E-Code 应用,发布该应用后就可以让该应用生效。

**应用ID**: 右键点击 E-Code 应用,选择 `属性` 可查看应用信息,其中应用ID分为 `appId` 和 `appUUID` , 需要注意 `appId` 是可变的,导入到其它 E10 系统后 ID 可能会变,而 `appUUID` 是不变的,所以在代码中需要使用应用 ID 时,尽量使用 `appUUID`。

编写组件

示例:在门户显示组件

1.编写连个组件,一个父组件和一个子组件

图片1

index.js

import ChildCom from './childCom';
    
export default function(){
  return <div>
    父组件
    <ChildCom />
  </div>
}

childCom.js

export default function (){
  return <div>子组件</div>
}

2.在 entry.js 里引入组件

在 entry.js 引入模块组件必需使用异步的方式

异步获取的组件必需放在 Suspense 元素中

const MyCom = lazy(() => asyncImport('${appId}', 'component/index'));

entry.js

import {render} from "react-dom";
import { useState, Suspense, lazy } from 'react';
import { asyncImport } from '@weapp/ecodesdk';
    
// 创建 App 挂载元素
const root = document.createElement('div');
document.body.appendChild(root);
    
// 样式处理,也可以新建 css 文件
const style = {
  position: 'absolute',
  zIndex: 9999,
  top: 100,
  left: '50%',
  transform: 'translate(-50%, 0)',
  fontSize: 60,
  fontWeight: 600,
  backgroundColor: 'rgba(255, 255, 255, 0.8)',
  boxShadow: '0 0 20px rgba(0, 0, 0, 0.5)'
};
    
const MyCom = lazy(() => asyncImport('${appId}', 'component/index'));
    
function App () {
  return (
    <Suspense fallback={<span>waiting</span>}>
      <div style={style}>
        <MyCom />
      </div>
    </Suspense>
    
  );
}
    
render(<App />, root);

效果:

图片2

组件获取、导入导出

在 entry.js 入口文件获取导出的组件

使用 `lazy` 和 `asyncImport` 异步获取组件

下面是导入在 `component/index` 文件下的 MyCom 组件示例

import { useState, Suspense, lazy } from 'react';
import { asyncImport } from '@weapp/ecodesdk';

const MyCom = lazy(() => asyncImport('${appId}', 'component/index'));

function App () {
  // 必需使用 Suspense 包裹
  return (
    <Suspense fallback={<span>waiting</span>}>
      <div style={style}>
        <MyCom />
      </div>
    </Suspense>
    
  );
}

在 entry.js 入口文件获取标准ui组件

比如我需要在标题中添加一个按钮,需要用 Button 组件,而入口文件是不能直接导入ui组件的,应该要怎么做

方法:新建一个组件,在组件内使用UI组件
import {Button } from '@weapp/ui';
    
function ExportButton (props) {
    return <Button {...props} type="primary">导出流程表单</Button>
}
    
export default ExportButton;
    

然后在入口文件导入新建的组件

import {useState, Suspense, lazy} from 'react';
    
const ExportButton = lazy(() => asyncImport('${appId}',
    'components/exportButton'));
    
function Button() {
    let {buttonName} = config;
    if(!buttonName){
        buttonName = '导出流程表单';
    }
    
    return (
        <Suspense fallback={<span>waiting</span>}>
            <div>
                <ExportButton onClick={() => handleClick()}>导出流程表单</ExportButton>
            </div>
        </Suspense>
    
    );
}

这样就可以使用 UI 组件了

/**
 * 添加顶部按钮
 */
regOvProps('weappUi', 'Title', (props) => {
    if (!location.href.includes('sp/workflow/report/customReportResult') || !currentPageEnable()) {
        return;
    }
    props.buttons.push(<Button/>)
    
}, 1);
方法:在非入口文件导出UI组件

非入口文件导出组件,可通过window.uiComs获取到导出的组件

import {Button,Dialog } from '@weapp/ui';
    
const coms = {
    Button,
    Dialog
}
    
window.uiComs = coms;
    
export  default coms;
    

要让导出组件生效,需要在另一个非入口文件中进行引入

import {Button } from '@weapp/ui';
import getComs from './getComs';
    
function ExportButton (props) {
    // 引入组件,使导出组件生效
    const coms = getComs;
    return <Button {...props} type="primary">导出流程表单</Button>
}
    
export default ExportButton;

在 entry.js 入口文件使用导出的UI组件

注意:确保先要让导出组件生效,比如 ExportButton 组件需要先执行

async function showMessage(type, content, delay) {
    if (!window.uiComs) {
        console.error('无法找到 uiComs');
        return;
    }
    const {Dialog} = window.uiComs;
    const obj = Dialog.message({type, content, delay});
    return obj.destroy;
}

asyncImport 和 jsonp 导入的区别

asyncImport 是异步导入组件,而 jsonp.require 是导入前置文件(entry.js) 中的对象

获取配置文件

配置编写在 /config/config.js 文件内,例如配置文件有以下内容:

const config = {
  width:'30%'
}
    
export default config;

需要使用`export`导出配置文件

获取配置文件:

使用`import`导入配置文件,注意 from 后面的路径,是相对于当前文件的路径,当前文件路径是根目录,所以使用 ./ ,./ 表示当前路径。

import config from './config/config'
// 使用配置文件
config.width;

组件参数复写

示例:将编辑按钮的文字改为红色

图片3

entry.js

import { regOvProps } from '@weapp/utils';
    
regOvProps('weappUi','Button',(props)=>{
  if(props.weId.endsWith('_tm0row')){
    
    props.style={
      ...props.style,
      color:'red'
    }
  }
  return props;
},1)

组件替换

示例:在门户添加一个菜单

图片4

entry.js 文件:

import React from 'react';
import { regOvComponent } from '@weapp/utils';
import { asyncImport } from '@weapp/ecodesdk';
    
const NewButton = React.lazy(() => asyncImport('${appId}', 'newMenu'));
    
regOvComponent('weappUi', 'Menu', (Com) => {
  return React.forwardRef((props, ref) => {
    // 需要根据 weId 去判断是否是要替换的组件,根据 noOverwrite 判断是否可重写,否则会无限循环造成卡死
    if (props.weId.endsWith('_39u13z')  && !props.noOverwrite) {
      return (
        <React.Suspense fallback={() => { }}>
          <NewButton ref={ref} {...props} OriginCom={Com} />
        </React.Suspense>
      )
    }
    // 如果不是要替换的组件需要返回原组件
    return <React.Suspense fallback={() => { }}>
          <Com ref={ref} {...props} />
        </React.Suspense>;
    });
}, 1)

newMenu.js

    
import { Menu } from '@weapp/ui';
    
const newMenu =(props,ref)=>{
  const { OriginCom } = props;
  // 添加新菜单
  const data = props.data;
  data.push({ id: 'custom-tab', content: '新添加的菜单' })
    
  // 可以使用别的组件,也可以用 <OriginCom /> 使用原组件
  return(
    <Menu {...props} data={data} noOverwrite={true} ref={ref}/>
  )
}
    
export default newMenu;
    

图片5

注意事项

在使用ecode复写组件时,如果需要限定复写某个组件时,区分weId时,请不要使用全路径匹配,weId是多级父子组件的weId拼接而成,不同场景,不同页面weId可能不一样。

正确做法是取weId最后一段以下划线结尾的字符串作为该组件的唯一标识符。

示例:

table组件weId值:`f56o40_9d1m87_ktrfwy_su9cgs_8d2ywu_i83sct`

正确判断方法:

regOvProps('weappUi',    'Table',    (props)    =>    {
        const    {weId=''}    =    props;
        if(weId    &&    weId.endsWith("_i83sct")){
                //逻辑处理
        }
        return    props;
},    0);

错误判断方法:

regOvProps('weappUi',    'Table',    (props)    =>    {
        const    {weId=''}    =    props;
        if(weId==='f56o40_9d1m87_ktrfwy_su9cgs_8d2ywu_i83sct'){
                //逻辑处理
        }
        return    props;
},    0);

接口拦截

使用 axios 进行拦截

有标准的拦截接口,如无必要不要使用 axios 拦截

请求拦截
// 前置拦截,获取请求数据
axios.interceptors.request.use(
  config =>{
    // config 包含所有请求信息
    const {url,params} = config;
    if(url.includes('api/hrm/employee/getTopMenu')){
      // 添加参数
      params.isTest = 1;
    }
    return config;
  },
  error =>{
    // 对请求错误做些什么
    return Promise.reject(error);
  }
)
响应拦截
// 后置拦截,获取接口返回数据
axios.interceptors.response.use(
  response =>{
    // response 里包含了响应信息,以及 config,使用 config 可以获取请求信息,data可以获取响应数据
    const {data,config:{url,params}} = response;
    if(url.includes('api/hrm/employee/getTopMenu')){
      // 能够获取到上面请求拦截中添加的参数
     const {isTest} = params;
      // 修改返回数据,添加一个按钮
      if(data.data){
        data.data.push({
          content:'新增按钮',
          id:'devBt'
        });
      }
    }
    return response;
  },
  error =>{
    // 对请求错误做些什么
    return Promise.reject(error);
  }
)
效果

添加了一个页签

图片6

注意事项

如果拦截有写 error 函数,则需要返回 Promise.reject(error)

公共插件

创建公共插件

公共插件组件 index.js:

import { Button,List} from '@weapp/ui';
const data=[
  {
    title:'内容1',
    id:1
  },
  {
    title:'内容2',
    id:2
  },
  {
    title:'内容3',
    id:3
  }
]
    
const MyPublicCom =()=>{
  return(
    <div>
      <div>
        <Button type={'primary'}>提交</Button>
      </div>
      <List
          sortable={true}
          direction="column"
          data={data}
          bordered={true}
          isStopMouseDown={true}
        />
    </div>
  )
}
    
export default MyPublicCom;

entry.js文件,导入上面的组件,并导出组件

在导出组件前要将组件放到 React.Suspense 内,因为组件是通过懒加载获取的

import { asyncImport } from '@weapp/ecodesdk';
    
const MyPublicCom = React.lazy(() => asyncImport('${appId}', 'index'));
    
function LazyMyPublicCom (props) {
  return (
    <React.Suspense fallback={() => {}}>
      <MyPublicCom {...props} />
    </React.Suspense>
  );
}
    
export {LazyMyPublicCom as MyPublicCom}

引入公共插件

方式一:使用 @weapp/ecodesdk.asyncImport 导入

在另外一个应用内,建立一个新页面,在页面内引入公共插件

使用 asyncImport 引入公共插件,asyncImport 可以引入其它模块非入口文件的组件,需要在 asyncImport 函数的第二个参数输入组件的文件路径

引入公共插件后需要将组件放入到 React.Suspense 元素内

import React from 'react';
import { Route } from 'react-router-dom';
import { regOvProps, appInfo, regReactChildren } from '@weapp/utils';
import { asyncImport, jsonp } from '@weapp/ecodesdk';
    
// 懒加载获取 index.js 里的组件,1002205076591648770 为应用id,index 为组件相对应用的路径
const MyPublicCom = React.lazy(() => asyncImport('1002205076591648770', 'index'));
    
const { publicUrl } = appInfo('@weapp/ecodesdk');
    
const newPage = (
  // publicUrl可能为空,前缀必需包含 /sp/custom/
  <Route path={`${publicUrl}/sp/custom/` + '${appId}/public-component-demo'}>
    <React.Suspense fallback= {() => { }}>
      <MyPublicCom />
    </React.Suspense>
  </Route>
);
    
// 注册路由
regOvProps('weappEcodesdk', 'Switch', (props) => {
  regReactChildren(props, newPage);
  return props;
}, 0);
方式二:使用@weapp/ecodesdk 的 jsonp.require 方法引用

使用 jsonp.require 方法引用时,只能引用入口文件导出的组件,所以在公共插件应用内必需在入口文件内导出组件。引用的组件不需要放入 React.Suspense 元素内,直接使用即可

import { jsonp } from '@weapp/ecodesdk';
import { regOvProps, appInfo, regReactChildren } from '@weapp/utils';
import { Route } from 'react-router-dom';
    
const { publicUrl } = appInfo('@weapp/ecodesdk');
    
// 导入公共插件,只能从入口文件导入
const {MyPublicCom} = jsonp.require('1002205076591648770','entry.js');
const newPage = (
  // publicUrl可能为空,前缀必需包含 /sp/custom/
  <Route path={`${publicUrl}/sp/custom/` + '${appId}/public-component-demo'}>
    <MyPublicCom />
  </Route>
);
    
// 注册路由
regOvProps('weappEcodesdk', 'Switch', (props) => {
  regReactChildren(props, newPage);
  return props;
}, 0);

新页面开发

entry.js 文件

import React from 'react';
import { Route } from 'react-router-dom';
import { regOvProps, appInfo, regReactChildren } from '@weapp/utils';
import { asyncImport, jsonp } from '@weapp/ecodesdk';
    
const NewPage = React.lazy(() => asyncImport('${appId}', 'newPage'));
    
const { publicUrl } = appInfo('@weapp/ecodesdk');
    
const newPage = (
  // publicUrl可能为空,前缀必需包含 /sp/custom/
  <Route path={`${publicUrl}/sp/custom/` + '${appId}/newPage'}>
    <React.Suspense fallback= {() => { }}>
      <NewPage />
    </React.Suspense>
  </Route>
);
    
// 注册路由
regOvProps('weappEcodesdk', 'Switch', (props) => {
  regReactChildren(props, newPage);
  return props;
}, 0);

newPage.js 文件

const NewPage = ()=>{
  return <div>新页面</div>
}
    
export default NewPage;

图片7

E-builder 开发

重写ebuilder组件

1.需要获取页面ID,页面id在链接中可以找到

图片8

2.获取要重写的ebuilder组件id

图片9

3.获取 weappEBpage 库的 PageView 组件参数,PageView 组件就是 ebuilder 页面里的容器组件,在里面做一个定时器,直到找到我们要的组件为止

import { asyncImport, jsonp } from '@weapp/ecodesdk';
import { regOvProps } from '@weapp/utils';
import ReactDOM from 'react-dom';
    
const CustomConentLazy = React.lazy(()=> asyncImport('${appId}','CustomContent'));
const CustomConent = <React.Suspense fallback={() => {}}>
    <CustomConentLazy />
  </React.Suspense>
    
// 组件id
const comId = 'ed08ca238c59466fb7a4f6b0103ab77e';
// 页面id
const pageId = '1002553806419304452';
    
function renderCom(dom){
  ReactDOM.render(CustomConent ,dom);
}
    
regOvProps('weappEBpage','PageView',(props)=>{
  if(props.pageId === pageId){
    let time=0;
    const interval = setInterval(()=>{
      // 找到对应的ebuilder组件,comId为组件id
      const dom = document.querySelector(`#${comId}`);
      if('ebuilderSDK' in window && dom){
        clearInterval(interval);
        renderCom(dom);
      }
      if(time++ > 50){
        clearInterval(interval);
        console.warn('未找到对应的e-builder组件')
      }
    },100)
  }
})

4.找到组件的 dom 元素之后,将我们自己写的组件渲染到这个 dom 元素

自定义组件

开发入口

在 eb 表单设计页面中,添加组件

图片10

图片11

图片12

进入组件开发

图片13

文件结构

进入ecode后会初始化这几个组件:

  • View.js : 用来显示组件内容
  • Config.js :定义组件配置,可在表单设计中显示该组件的配置
  • Disign.js :在表单设计器中将该组件拖拽到表单后,显示的组件内容
Config.js 组件配置文件

items 是组件的配置项,这些配置会显示在表单设计器中的组件参数中

属性说明:

  • label :配置项名称
  • itemType :配置项组件类型,可通过此文档查看可用的组件类型,Form 表单 (位置:FormItemProps 说明),可用类型有:

| 'RATE' | 'UPLOAD' | 'SWITCH' | 'COLORPICKER' | 'SCOPE' | 'BROWSER' | 'CUSTOM' | 'DESCRIPTION' | 'TYPESBROWSER'

| 'DATETIMEPICKER' | 'DATEQUICKFILTER' | 'LOCALE' | 'RICHTEXT' | 'PASSWORD' | 'SLIDER' | 'SIGNATURE' | 'ICONSELECTION' | 'BROWSERWINDOW'

| 'MONTHDAYPICKER' | 'RANGE' | 'FILTERBUTTONS' | 'CASCADER' | 'SHAREBROWSER' | 'RICHICONSELECTION' | 'PLUGIN'

  • helpTip : 提示内容,可在表单设计器中的组件参数配置中显示问号,问号的内容会显示 helpTip 的内容
const Config = (data, store) => {
  return Promise.resolve({
    items: {
      detailKey: {
        label: '数据来源明细key ',
        itemType: 'INPUT',
      },
      idFieldName: {
        label: '唯一标识字段名',
        itemType: 'INPUT',
        helpTip:'请填入数据库字段名'
      },
      parentIdFieldName: {
        label: '上级标识字段名',
        itemType: 'INPUT',
        helpTip:'请填入数据库字段名'
      },
      sumFieldNames:{
        label: '合计字段名',
        itemType: 'INPUT',
        helpTip:'合计下级的字段值到上级字段,请填入数据库字段名,多个使用英文逗号分隔'
      },
      dataLinkageFileName:{
        label: '明细表数据字段联动字段名',
        itemType: 'INPUT',
        helpTip:'选填,填主表字段名,比如有一个主表字段为浏览框,选择后带出明细数据,在执行该字段联动后将会更新明细树数据'
      }
      
    },
    formatFormDatas: (_data) => {
      return {
        ..._data,
      }
    }
  });
};
    
export default Config;

效果:

图片14

可在 View.js 中使用配置

export default function View(props) {
  // 获取配置
  const { config } = props;
  const { detailKey, idFieldName, parentIdFieldName, sumFieldNames,dataLinkageFileName } = config;
  
  // ... 组件内容
}

示例

在 View.js 中导入了一个组件

import React from 'react';
import {asyncImport} from '@weapp/ecodesdk';
    
const Table = React.lazy(() => asyncImport('${appId}','components/Table'))
    
const View = (props) => {
  const { config } = props;
    
  return (
    <div className="cusStyleCls">
      <React.Suspense fallback={()=>{}}>
        <Table />
      </React.Suspense>
    </div>
  );
};
    
export default View

这里我定义了一个组件,作为组件的现实内容,在 View.js 中使用

图片15

效果:

图片16

流程

表单 SDK

getFieldInfo() 获取字段信息

使用此接口可以获取字段信息,可以用于将字段信息传入 `FormSwitch` 组件中来复制字段

结果示例:

{
    "childName": "",
    "componentKey": "Select",
    "content": "",
    "fieldId": "1263690721244209153",
    "flow": {
        "parent": "HpMjIxDAwP",
        "nodes": [],
        "isCanvas": false,
        "linkedNodes": {},
        "nodeId": "keGxB2-tRd"
    },
    "hasChild": false,
    "isDefault": false,
    "isHideTitle": false,
    "layout": {
        "selected": true,
        "offsetTop": 635
    },
    "name": "下拉单选框",
    "parentName": "",
    "size": "large",
    "subFormId": "1245174301386883072",
    "templateName": "控制方式",
    "hideScore": true,
    "optionBackgroundStyle": "default",
    "groupId": "-1",
    "templateId": "1263690953222774786",
    "title": "控制方式",
    "type": "Select",
    "dataCount": 1,
    "selectedNum": "",
    "dataKey": "kzfs",
    "publicTemplate": false,
    "systemField": true,
    "columnTypeSwitch": false,
    "options": [],
    "showType": "Select",
    "placeholder": "",
    "formTableId": "1245174322802999297",
    "typeLen": 64,
    "formId": "1242178016102858766",
    "templateOptionNum": {},
    "templateScore": {},
    "parentId": [],
    "columnType": "VARCHAR",
    "showOrder": 0,
    "hasTemplate": true,
    "columnName": "kzfs",
    "disableEdit": false,
    "display": true,
    "singleRow": false,
    "readOnly": false,
    "required": false,
    "autoFocus": false,
    "readOnlyPlaceholder": "暂无内容",
    "__IsFirstWithRow": true
}

归档节点使用 SDK 获取字段属性判断问题

归档节点表单渲染字段都是只读的,如果表单设计器中将字段设为编辑,SDK 获取的显示属性依然是编辑,需要使用 `formSdk.getBaseInfo.isEditable` 配合判断表单是否整体可编辑,如果不可编辑则认为字段是只读的

流程表单开发

新建流程保存后触发

表单js-SDK 的表单加载完成后回调事件和流程 js-SDK 的 Ready 加载事件都不会在流程新建保存后触发,如果是用 ecode 开发,需要使用`regHook`,配合`Ready`可实现只要表单载入时就触发,包括表单二次加载(比如保存时)。

参考:流程详情-JSSDK文档见1.4

// 表单载入时触发
const wffpSdk = window.weappWorkflow.getFlowPageSDK();
wffpSdk.ready(()=>{
  onFormLoadedAction();
});
    
// 注册表单载入时触发
regHook('weappWorkflow', 'FPMainTabWillMount', onFormLoadedAction);
    
function onFormLoadedAction(params) {
  console.log('执行 onFormLoadedAction')
  const wffpSdk = window.weappWorkflow.getFlowPageSDK();
  
  // 如果需要立即获取到 formSdk,则需要添加 ready
  wffpSdk.ready(() => {
    const formSdk = window.WeFormSDK.getWeFormInstance();
    
    }
  });
}

获取流程节点id

1. 在前端中新建流程 2. 打开浏览器中的开发者工具,并在工具中切换到网络标签页 3. 在流程中切换到流程图标签页 4. 在开发者工具中找到名称为 getXml 接口,选中该接口在右侧中点击预览标签页,查看该接口的返回信息 5. 在返回信息中找到 data -> chartFlowInfo -> normalChartInfoEntity -> nodeInfo 6. 在 nodeInfo 中包含了各节点的信息,里面的 nodeId 就是节点id

图片17

流程详情二开口子

流程详情-组件复写-二开口子

流程表单标准接口

表单数据接口

包含表单全部字段数据和表单页面布局 接口地址:`/api/workflow/core/form/core/getFormDataAndLayout?formId=1151612316977602563&dataId=1247741669520719874

返回数据说明:

  • 字段数据存放在:formData.dataDetails 中
  • 表单字段组件数据(主表和明细),包括字段显示名和字段id:formLayout.pageLayouts.comps

表单附件下载接口

接口地址:`http://e10oa.bodatek.net:20600/api/file/remotedownload/[字段id]/upload/true?module=workflow&authModule=workflow&source=form&fieldId=[附件id]`

调用此接口可下载表单文件字段中的附件

表单开发

字段自定义渲染

可自定义字段的显示内容,使用表单 SDK 的`proxyFieldComp`实现,示例:

参数1: 字段id

`createElement`的第三个参数可传入 React 组件,为字段的显示内容

formSdk.proxyFieldComp(`${budgetInfoFieldMark}_${rowId}`, React.createElement('div', { budgetInfoFieldMark },
    <BudgetInfo totalBudget={totalBudget}
      availableBudget={availableBudget}
      usedCost={usedCost}
      approvalBudget={approvalBudget} />));

加载 ecode

在流程高级设置里可以添加 ecode ,这里说的是另一种方法,可以在代码块里加载任意 ecode 应用

e10没有像e9那样可以使用 ecodeSDK.load() 加载 ecode ,不过流程表单代码块也是支持js模块化的,可以导入ecode的组件

可以在ecode中创建一个类,类中写代码逻辑,然后在流程中导入这个类去执行

示例:

在ecode中创建一个类,index.js

class DetailHrColControl {
    // 类中写代码逻辑
    doSomeThing(){
	    // 获取表单数据...
    }
}
export default DetailHrColControl;

在流程中引入类:

import { asyncImport,jsonp } from '@weapp/ecodesdk';
// 第一个参数是应用id,第二个参数是ecode中的组件所在文件名称
asyncImport('1149493253298642947','index').then(esmodule =>{
    // 相当于:new DetailHrColControl()
  const obj = new esmodule.default();
  // 执行代码
  obj.doSomeThing();
});

明细表加载完后触发

这个没有标准接口支持,可以在表单载入时创建一个定时器,反复判断明细表是否有数据,可通过`formSdk.getDetailAllRowIndexStr(detailMark)`来判断明细表是否有数据,下面是一个示例,当明细表加载完成之后执行回调函数

function onDetailReady(detailKey, callback) {
  const maxTime = 10000;
  const timeout = 100;
  let count = 0;
  const formSdk = window.WeFormSDK.getWeFormInstance();
    
  const interval = setInterval(() => {
    count++;
    const detailMark = formSdk.convertFieldNameToId(detailKey);
    const rowIdStr = formSdk.getDetailAllRowIndexStr(detailMark);
    if (rowIdStr) {
      callback(rowIdStr);
      clearInterval(interval);
    }
    if (count * timeout > maxTime) {
      console.warn('等待明细加载超时');
      clearInterval(interval);
    }
    
  }, timeout);
    
}
    

字段复制

可以使用 `FormSwitch` 组件复制字段,比如复制整个明细,详细可以看 FormSwitch - 表单组件分发

组件使用

Radio

如果要创建一组单选框,则需要在Radio组件添加 data 参数

data 参数格式如下:

const options = [
  {
    id: 'Apple',
    title: 'Apple',
    content: 'Apple',
    color: '#5D9CEC'
  },
  {
    id: 'Pear',
    title: 'Pear',
    content: 'Pear',
    color: '#FF4D4F'
  },
  {
    id: 'Orange',
    title: 'Orange',
    content: 'Orange',
    disabled: true,
    color: '#52C41A'
  },
];

如果 Radio 单独使用,则每个选择框都是独立的,不能实现单选

<Radio readOnly
          onChange={this.onChange}
          onClick={this.onClick}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
        >只读</Radio>
        <Radio readOnly
          value={true}
          onChange={this.onChange}
          onClick={this.onClick}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
        >只读2</Radio>

示例

在签字意见上方添加单选框

import { RichText, Radio } from '@weapp/ui';
import config from './config/config';
    
    
function getOptions(){
  // 从配置中获取选项
  const configList = config.workflowRemarkOptionConfig
  const weFormSdk = window.WeFormSDK.getWeFormInstance();
  const formInfo = weFormSdk.getBaseInfo();
  const optionConfig = configList.find(i => i.formId === formInfo.formId);
  if(optionConfig){
    const optionTexts = optionConfig.options;
    let id = 1;
    return optionTexts.map(i =>{ return {id:id++,title:i,content:i} });
  }
  return [];
}
    
/**
 * 在签字意见顶部添加单选框
 */
const NewRichText = (props, ref) => {
  const { OriginCom } = props;
    
  const onChange = (v, o, e) => {
    if(v){
      // 修改签字意见
      weappWorkflow.getFlowPageSDK().changeRemark(o.content);
    }
  }
    
  return (
    <div>
      <div style={{padding:10,display:'flex',alignItems:'center'}}>
        <span>签字意见</span> 
        <div style={{marginLeft:30}}>
          <Radio data={getOptions()} onChange={onChange} />
        </div>
      </div>
      <RichText {...props} noOverwrite={true} ref={ref} />
    </div>
  )
}
    
export default NewRichText;

FormSwitch - 表单组件分发

此组件可用于在表单中复制已有的表单字段,比如复制出一个已有的明细 组件说明:e-cology.com.cn/sp/form/components/FormSwitch?ticket=ST-7374-UZ1hsJZqHEs3adc7Vmz1-https://www.e-cology.com.cn

案例:Ecode-表单明细树组件

标准页面修改

动作流

动作流页面组件参数说明

动作流数据接口

地址:/api/bs/esb/setting/flow/design/getDesignBySceneId/1279306359223615490

**返回数据关键说明**

  • actions 里包含了各个动作组件的数据,例如:

这是一个 serverless/Action 组件的数据

{
 "actions": [
	            {
	                "id": "2064625614526222352",
	                "wId": "2064625614526222352",
	                "name": "serverless/Action",
	                "type": "ServerlessAction",
	                "typeName": "serverless/Action",
	                "attr": {
	                    "icon": "Icon-codeblock"
	                }
	            }
            ]
}
  • wId 为组件id
获取 Action 参数对话框中的赋值信息

可通过获取当前 `Dialog` 组件的参数,获取参数中的 `props.children.props.assignProps` 数据,这些就是对话框中的参数赋值信息,其中可通过 `assignProps.assignContext.wId` 获取到当前动作组件的 wId 。

使用方法

以下是如何复制一个表单字段的用法: 需要使用 `CorsComponent` 组件,在 `compName` 参数中指定 `FormSwitch` , 示例:

 <CorsComponent
      app="@weapp/formbuilder"
      compName="FormSwitch"
      config={fieldConfig}
      isNewDataFormat
      value={valueToRender}
      onChange={handleChange}
    />

**config 参数** : `config` 参数是字段的属性,决定这个字段是什么样的,可以使用 `formSdk.getFieldInfo(fieldId)` 来获取字段属性,放到 `config` 参数中,其中里面的 `readOnly` 和 `disabled` 决定了这个字段的只读属性,如果字段只读需要将这两个属性赋值为 true **请注意**:通过 `formSdk.getFieldInfo(fieldId)` 获取的 `readOnly` 并不准确 ,请使用表单 sdk 来获取字段编辑属性

**value 参数** : 参数格式参见文档 该参数表示字段的值,通过表单 SDK 获取的字段值需要进行转换,才能放入 value 参数中,转换示例:

/**
 * 因为从明细中获取的字段值与表格中数据的字段值格式不一致,需要转为表格中的字段数据结构
 */
function getTargetFieldData(data) {
  // 如果存在 specialObj 数组,将 id 用逗号拼接作为 content(如果 content 尚未设置)
  if (data.specialObj && Array.isArray(data.specialObj) && data.specialObj.length > 0) {
    // 优先使用已存在的 content,否则使用 id 拼接
    if (!data.content) {
      data.content = data.specialObj.map(item => item.id).join(',');
    }
  } else if (!data.content) {
    // 如果没有 specialObj 且 content 尚未设置,使用 value
    data.content = data.value;
  }

  // 处理下拉框/浏览按钮类型的字段
  // 如果有 specialObj,转换为 dataOptions 格式
  if (data.specialObj && Array.isArray(data.specialObj)) {
    data.dataOptions = data.specialObj.map(item => ({
      optionId: item.id,
      content: item.name
    }));
  }

  // 不同字段类型有不同的数据格式,可以根据 fieldType 字段类型转换为对应字段类型的数据格式
  return data;
}

**onChange参数**: 当值改变时会调用 onChange(value) ,value 为变化后的值,与组件的 value 参数的值格式一样。 示例:

 const handleChange = (newValue) => {
    // 获取当前字段数据
    const fieldData = rowData[field.fieldId] || {};

    // 如果有 dataOptions,转换为 specialObj 格式
    let specialObj = null;
    if (newValue.dataOptions && Array.isArray(newValue.dataOptions)) {
      specialObj = newValue.dataOptions.map(opt => ({
        id: opt.optionId,
        name: opt.content
      }));
    }

    // 数据更新到表单明细
    if (!rowData.isVirtual) {
      const fieldMark = `field${field.fieldId}_${rowData.rowId}`;
      const updateData = { value: newValue.content };
      // 如果有 specialObj,添加到更新数据中
      if (specialObj) {
        updateData.specialObj = specialObj;
      }
      formSdk.changeFieldValue(`${fieldMark}_${rowData.rowId}`, updateData);
    }
  };

案例

流程签字意见放到右边

ecode-流程签字意见放到右边

流程页面添加按钮

案例-流程表单页面添加按钮

明细表树组件

Ecode-表单明细树组件

问题汇总

entry.js 文件里不能导入模块组件问题

问题现象

无法获取到模块组件

图片18

图片19

解决方法

再新建一个项目,在里面重写旧项目的代码,发布