<!-- source: https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/repository/b7c818b1-9132-46a7-bfaf-78fc6747791a/doc/440700815435366400 -->

## 相关文档汇总

1、[E10低代码平台开发手册](https://weapp.eteams.cn/ecode/playground/doc/share/view/1081969229081370625/freepass) 2、[E10平台定制开发指引](https://www.e-cology.com.cn/ecode/playground/doc/share/view/916733818655342593) 3、[二次开发说明规范文档](https://weapp.eteams.cn/ecode/playground/doc/share/view/974701716410515476) 4、[建模表单/表格SDK](https://weapp.eteams.cn/ecode/playground/doc/share/view/990693279847899137) 5、[表单引擎JS-SDK](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/48df67c6-1187-462e-99ec-ceb6617c10ff) - 注：更推荐在表单设计器的代码块左侧查看相关方法 6、[流程详情JS-SDK](https://weapp.eteams.cn/ecode/playground/doc/share/view/974701716410515525) 7、[列表组件插件包](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/repository/6d237e77-b6b2-4c50-a66e-b7b727a97050/home) - 注：大部分EB组件插件包可在[EB组件文档](https://www.e-cology.com.cn/sp/ebddoc)中获取 8、[EM JS-SDK](http://emdoc.wx.weaver.com.cn/web/#/3/701) 9、[e10流程模块二开口子汇总](https://weapp.eteams.cn/ecode/playground/doc/share/view/969524129164623876) 10、[e-cology10国际化二次开发手册](https://www.e-cology.com.cn/sp/doc/docDetail/100500240014988616) 11、[可编辑列表二开](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/dca83d7a-35d3-449f-9e10-bc9eb4bd0553) 12、[ebuilder页面前端sdk](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/online/722818617793552384/doc/722819171810779137) - 注：更推荐在页面设计器的代码块左侧查看相关方法

## 通过ecode实现功能的方案

可参考标准文档中"[判断需求应该可以用什么方式实现](https://weapp.eteams.cn/ecode/playground/doc/share/view/974701716410515476#1.2.7%20%E5%88%A4%E6%96%AD%E9%9C%80%E6%B1%82%E5%BA%94%E8%AF%A5%E5%8F%AF%E4%BB%A5%E7%94%A8%E4%BB%80%E4%B9%88%E6%96%B9%E5%BC%8F%E5%AE%9E%E7%8E%B0)"

1、拦截数据 - regOvProps - pluginCenter - regApiInterceptor(axios.interceptors的二开标准实现，见[e-code二开请求和拦截说明文档](https://weapp.eteams.cn/ecode/playground/doc/share/view/1173606561081745409))

2、拦截数据的展示方式 - regOvComponent - regOvProps - pluginCenter

3、在特定的时间点做某些操作 - eventEmitter - pageSdk.on - pluginCenter - regOvComponent

## 找寻实现方案 & 排查问题

1、借助react developer tools([如何查找可开发的标准组件](https://www.e-cology.com.cn/ecode/playground/doc/share/view/916733818655342593#2.2%20%E5%A6%82%E4%BD%95%E6%9F%A5%E6%89%BE%E5%8F%AF%E5%BC%80%E5%8F%91%E7%9A%84%E6%A0%87%E5%87%86%E7%BB%84%E4%BB%B6))；

2、涉及到数据，使用sdk等判断： formSdk - 表单引擎sdk ebdfPageSdk (cardSdk、listSdk) - 建模sdk ebuilderSDK - ebSdk pageSdk - eb页面sdk wffpSdk - 流程sdk

3、屏蔽ecode - network request blocking拦截ecode资源 [(前端二次开发规范 1.2.6)](https://weapp.eteams.cn/ecode/playground/doc/share/view/974701716410515476#1.2.6%20%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E6%98%AFecode%E5%BD%B1%E5%93%8D%E5%8E%9F%E6%9C%89%E5%8A%9F%E8%83%BD) 这里需要注意，页面设计器中的事件js不属于ecode，所以无法拦截到，有些时候你需要去看看页面以及页面内组件是否有事件来控制（如果是事件中触发的问题且没有想到这里的话会很难排查）

4、借助后文中debug tools针对特定的复写进行去除精确找到问题所在

## 底层实现方案相关，及思考后的封装方法参考

### 1、ecode加载时机：

全局ecode - 仅执行一次，不需要考虑太多生命周期相关（需考虑前置加载带来的性能问题）
页面代码块 - 每次页面加载时都会执行一次（页面加载前会执行完毕）
表单代码块 - 每次表单加载时都会执行一次
表格代码块 - 每次建模表格加载完毕执行一次，但加载完后有延迟
事件js - 每次事件执行时触发（较特殊，通过eval实现，严格来说不属于ecode）### 2、在页面代码块 / 表单代码块中使用：

eventEmitter / regOvProps / regOvComponent 
    每次执行时都会在全局挂载事件，存在性能问题 & 会有潜在的闭包风险，较难排查
    需要考虑如何在页面关闭时卸载事件，或者避免重复卸载（视情况而定）
使用时避免重复卸载的方案：
    在第二次挂载时，卸载上一次的注册
	在第二次挂载时，检测到有注册则不注册
    跟随组件卸载而卸载(regOvComponent)### 3、middleware的实现原理

*regOvProps / regOvComponent* （1）标准组件使用@middleware包裹，加入wrap层 （2）调用regOvProps / regOvComponent时，注册事件 （3）实际渲染时，wrap层从注册的事件中调用并传入方法，决定实际渲染的效果

function wrap(app, name, Com) {
  let Wrap = React.forwardRef((props, ref) =\> {
    let NewCom = Com;
    let newProps = props;
    // 仅展示核心逻辑，实际上这里包含错误处理等
    // 获取注册的props和component是通过getSyncHook实现的
    // hook相关见jsApi文档：https://www.e-cology.com.cn/utils/jsApi
    const newProps = getOvProps(props);
	const NewCom = getOvComponent(Com);
    return \<NewCom ref={ref} {...newProps} /\>;
  });
  Wrap.displayName = \`${app}.${name}\`;
  return Wrap;
};

function middleware(app, name) {
	return (Com) =\> wrap(app, name, Com);
};

function regOvProps(app, name, fn, order) {
	// 在对应app & name下注册方法
	// ...
};

function unRegOvProps(app, name, fn) {
	// 获取对应app & name下所有方法，遍历找到全等的方法并去除
	// ...
};综上：所有调用逻辑需要在组件渲染之前注册完毕才能生效 & 卸载时必须提供原注册方法

#### 工具方法：

基于middleware的实现原理，构建了以下的工具方法： *注意⚠️：需避免在实际生产环境中==****滥用****==封装后的方法，除非你对于整体的注册逻辑能捋得很顺，否则排查时可能会很困难；尽可能从业务角度上解决重复注册的问题*

##### 1、unRegAll

(用于debug)(注册的所有方法都可以通过getOverWrite拿到，也可用于针对性卸载某个二开 / 获取所有的二开注册)

/\*\*
\* debug tools
\* 去除指定libName和compName的所有二开拦截
\*/
const unRegAllOvProps = (libName, compName) =\> {
	const { getOverwrite, unRegOvProps } = weappUtils;
	const allRegOvProps = getOverwrite(libName, compName)\[libName\]\[compName\]\['ovProps'\];
	allRegOvProps.forEach(reg =\> {
		unRegOvProps(libName, compName, reg.fn);
	});
};

const unRegAllOvComponent = (libName, compName) =\> {
	const { getOverwrite, unRegOvComponent } = weappUtils;
	const allRegOvComponent = getOverwrite(libName, compName)\[libName\]\[compName\]\['ovComponent'\];
	allRegOvComponent.forEach(reg =\> {
		unRegOvComponent(libName, compName, reg.fn);
	});
};
##### 2、middleware封装

/\*\*
\* 封装regOvProps
\* 主要解决问题：注册前卸载已注册的方法，避免重复注册
\* libName: 仓库名
\* compName: 组件名
\* regFn: 复写方法，可接收匿名函数
\* fnId: 方法ID，默认取eb页面id / pathname
\* priority: 复写优先级，默认为0
\*/

const customPropsMiddleware = (
	libName,
	compName,
	regFn,
	priority = 0,
	fnId = window?.ebuilderSDK?.getPageSDK?.()?.pageId || window?.location?.pathname,
) =\> {
	const { regOvProps, unRegOvProps, getHooks } = window.weappUtils;
	// 卸载之前注册的方法
	const targetFns = getHooks()?.\[\`middleware.${libName}\`\]?.\[\`${compName}.ovProps\`\] || \[\];
	targetFns.some((regedFn) =\> {
		const fn = regedFn?.fn;
		if (fn && fn?.\["ecode\_sjrProjectEB\_regFnId"\] === fnId) {
			unRegOvProps(libName, compName, fn);
			return true;
		}
		return false;
	});
	// 方法给唯一id
	Object.defineProperty(regFn, "ecode\_sjrProjectEB\_regFnId", {
		value: fnId,
		writable: false,
	});
	return regOvProps(libName, compName, regFn, priority);
};

/\*\*
\* customComponentMiddleware(
\* libName,
\* compName,
\* regFn,
\* priority(复写优先级，默认0),
\* fnId(方法id，用于定位该复写的方法),
\* replace(替换原方法，false表示不重复注册)
\* )
\* \*/
const customComponentMiddleware = (
	libName,
	compName,
	regFn,
	priority = 0,
	fnId = window?.ebuilderSDK?.getPageSDK?.()?.pageId || window?.location?.pathname,
	replace = true
) =\> {
	const { regOvComponent, unRegOvComponent, getHooks } = window.weappUtils;
	const targetFns = getHooks()?.\[\`middleware.${libName}\`\]?.\[\`${compName}.ovComponent\`\] || \[\];
	const targetFn = targetFns.find((regedFn) =\> {
		const fn = regedFn?.fn;
		if (fn && fn?.\["ecode\_sjrProjectEB\_regFnId"\] === fnId) {
			return true;
		}
		return false;
	});
	if (targetFn) {
		if (replace) {
			unRegOvComponent(libName, compName, targetFn.fn);
		} else {
			return;
		}
	}
	Object.defineProperty(regFn, "ecode\_sjrProjectEB\_regFnId", {
		value: fnId,
		writable: false,
	});
	return regOvComponent(libName, compName, regFn, priority);
};##### 3、eventEmitter封装

/\*\*
\* 封装eventEmitter
\* 主要解决问题：注册前卸载当前id对应事件，避免重复注册
\* libName: 仓库名
\* funcName: 事件名
\* regFn: 事件方法，可接收匿名函数
\* type: 'on' | 'once'，不传为'on'
\* fnId: 方法ID，默认取eb页面id / pathname
\*/
const customEventEmitter = (
	libName,
	funcName,
	regFn,
	type,
	fnId = window?.ebuilderSDK?.getPageSDK().pageId || window?.location?.pathname
) =\> {
	const { eventEmitter } = window.weappUtils;
	const targetFns = eventEmitter.listeners(libName, funcName);
	targetFns.forEach((regedFn) =\> {
		if (regedFn?.\['ecode\_sjrProjectEB\_regFnId'\] === fnId) {
			eventEmitter.off(libName, funcName, regedFn);
		}
	});
	Object.defineProperty(regFn, "ecode\_sjrProjectEB\_regFnId", {
		value: fnId,
		writable: false,
	});
	return type === 'once'
		? eventEmitter.once(libName, funcName, regFn)
		: eventEmitter.on(libName, funcName, regFn);
};### 4、方便开发调试的浏览器脚本

ec产品中从某个基线开始默认屏蔽了所有console信息的展示，由于开发及调试过程中经常会需要用到console，每次都需要去路径后拼接太麻烦了，就写了一个小的脚本来实现console的默认开启。 获取地址： greasyfork(未开启公开，只能通过链接进入): [weaver-tools-shared](https://greasyfork.org/zh-CN/scripts/538962-weaver-tools-shared) greasyfork镜像站(无需vpn): [weaver-tools-shared](https://gfork.zh-tw.eu.org/zh-hans/scripts/538962-weaver-tools-shared)

## 实际场景

### 1、九宫格 / 表格等针对单个字段进行渲染

*实现方案在新基线上有较多可以优化的地方，如使用ecode的加载顺序功能来实现字段id字典的直接使用，部分逻辑也可以变为异步加载等；实际代码可在EB项目管理搭建团队ecode应用“组件字段级自定义渲染”中查看，仅供思路参考*

#### 1.1 需求

项目管理应用中，紧急程度和状态为包含颜色字段的可配置的ebuilder表单，在项目/任务基础表中展示为关联ebuilder字段；需要在展示项目、任务数据（可能为列表、卡片等）时，将对应的字段展示为特定的效果，除去这两个字段之外，还需要以特定效果展示时间字段等。

#### 1.2 需求拆解

1、需要在很多页面的不同组件中将特定数据源中的特定字段展示为特定效果。 2、特定效果需要异步获取数据实现。

#### 1.3 实现方案

1、由于实际的渲染逻辑一致且需要在同应用内大量使用，可将大部分逻辑抽离至全局ecode中 - 全局配置获取 & 渲染 & 组件拦截注册 2、考虑页面映射情况以及方法复用性，仍然需要在页面代码块等实际使用的地方进行事件注册 3、全局配置获取需要异步，避免性能问题（最早的方案数据获取使用动作流，若默认全局请求会因为动作流性能问题导致宕机） 最终实现方案的代码结构： （1）regFunc.js - 抛出方法供外部使用，记录需要走渲染的组件id及需要渲染的字段 & 处理不同组件内不同的渲染方案 （2）renderFields.js - 处理字段的实际渲染逻辑 （3）requestConfig.js - 获取配置数据，处理缓存

#### 1.4 实现代码

/\*\*
\*\* regFunc.js
\*\* 这里处理渲染的注册，以及组件的渲染拦截逻辑
\*\*/
// 结构: {compId: Set(\[fieldIds\])}
let needRegCompIds = {};
const appendRegField = (compId, fieldName) =\> {
	let fieldNameSet = needRegCompIds?.\[compId\] || new Set();
	if (Array.isArray(fieldName)) {
		fieldName.forEach(name =\> fieldNameSet.add(name));
	} else {
		fieldNameSet.add(fieldName);
	}
	needRegCompIds = {
		...needRegCompIds,
		\[compId\]: fieldNameSet
	}
};
const regFieldFunc = (Com) =\> {
	return forwardRef((props, ref) =\> {
	const targetCompId = Object.keys(needRegCompIds).find(i =\> props?.weId?.indexOf(i) \> -1);
	if (targetCompId) {
		// 根据props中的id等信息确定渲染的字段，和需要的效果
	}
	return \<Com {...props} ref={ref} /\>;
	});

};
// 这里注册时还需要注册不同的组件 & 如果为table需要用regOvProps拦截columns等等，然后在实际的渲染中根据props的不同来处理不同的渲染逻辑
regOvComponent('weappEbdlist', 'FieldCustomRender', regFieldFunc, 33);
window.sjrProjectSDK = {
	...window.sjrProjectSDK || {},
	appendRegField,
};

/\*\* 
\*\* renderFields.js
\*\* 这里处理字段实际的渲染逻辑，异步配置需要用state获取
\*\*/
const Status = (props) =\> {
	const \[color, setColor\] = useState('');
	const \[bgColor, setBgColor\] = useState('');
	useEffect(() =\> {
		sjrProjectConfig.get(statusType + 'Status').then(res =\> {
			const targetConfig = res.find(config =\> config.id === id) || {};
			if (targetConfig\['font\_color'\]) {
				setColor(targetConfig\['font\_color'\]);
			}
			if (targetConfig\['background\_color'\]) {
				setBgColor(targetConfig\['background\_color'\]);
			}
		});
	}, \[id\]);
	// 处理实际渲染逻辑
	return \<div\>\</div\>;
};

/\*\*
\*\* requestConfig.js
\*\*/
class SJRProjectConfig {
	configData = {};
	requestPromise = null;
	
	constructor() {
		// 处理缓存，若实时性要求不强可考虑从parent / opener拿
	};
	
	doGetData = () =\> {
		if (this.requestPromise) {
			return this.requestPromise;
		}
		this.requestPromise = new Promise((resolve, reject) =\> {
			// 这里处理实际数据的获取逻辑
			const response = await getData();
			this.configData = response;
			resolve(response);
			this.requestPromise = null;
		});
		return this.requestPromise;
	};
	
	get = async (key) =\> {
		if (isEmpty(this.configData)) {
			await this.doGetData();
			return key ? this.configData\[key\] : this.configData;
		}
		return key ? this.configData\[key\] : this.configData;
	}
}

/\*\*
\*\* 实际页面使用
\*\*/
sjrProjectSDK.appendRegField(compId, \[taskBaseStatus, taskBasePriority\]);## 常见问题

### 1、ecode不执行？ecode代码有问题？

ecode实际执行的代码和代码设计器中的内容存在差距，实际执行会拼接特定的模板代码并转为es5语法，故代码设计器中有代码或者代码设计器中代码正确不代表实际执行的时候执行的是对应的代码。 该问题常见于页面设计器、表单设计器、建模表格，全局中目前没有遇到过类似问题。 1.1、页面ecode代码排查方式： （1） 确定当前加载的页面是对应的页面，可根据元素中的id来判断 !\[\[Pasted image 20260209153508.png\]\] （2）search中根据对应id搜索即可找到页面代码块，可排查是否成功加载以及内容是否正确!\[\[Pasted image 20260209153654.png\]\]

1.2、表单ecode排查方式 与页面代码块相同，表单ecodeid在getFormDataAndLayout / getFormLayout接口中，由于部分情况下表单引擎可能会将对应的配置存放在indexDB中而不在接口中返回，可通过搜索代码片段的方式定位到相关代码 !\[\[Pasted image 20260209154138.png\]\]

1.3、表格ecode排查方式 appid在接口/api/ebuilder/form/codeblock中返回，建议通过搜索代码片段的方式定位 !\[\[Pasted image 20260209154347.png\]\]

### 2、插件包注册后不生效

该问题仅适用于：断点确认插件包注册逻辑有走 & 插件包内的触发逻辑也有走 & 插件包逻辑不生效的情况。 目前这个问题在处理进展看板组件中遇到过，具体原因是因为进展看板组件本身就是通过插件包来实现的，如果注册的插件包有和标准的冲突，则会根据加载顺序以后注册的为准，需要断点确认组件内调用插件包时到底触发了几个事件，可通过定时器等方式来实现延迟加载以保证自己注册的插件包优先级更高。

### 3、ecode加载顺序

3.1 全局ecode控制加载顺序 目前已经可以通过修改应用属性来实现，以便更好地组织代码 !\[\[Pasted image 20260209154826.png\]\] 3.2 页面代码块 - 根据经验基本可以确定页面加载前ecode都可以执行完毕 3.3 表格代码块 - 表格加载完毕后js才会开始执行，在这里写代码时需要额外注意 3.4 表单代码块 - 表单加载时执行，没有研究过绝对顺序，但是一般在这里直接调用formSdk是安全的，应该是数据返回后执行（注意：这里是指表单，而不是建模表单，例如按钮等属于是建模表单的业务，在这里写js无法确定执行顺序） 3.5 事件js - 点击时触发

### 4、ecode中使用id如何迁移

4.1 全局ecode需要绑定应用关联，跟随应用迁移，注意只可以用对应应用中的数据id，也不能确定所有数据id都会被迁移，如果需要补充可以联系相关id对应模块的开发 !\[\[Pasted image 20260209155449.png\]\] 4.2 其它地方的ecode不需要单独关联，会默认跟随应用迁出

这里建议的编码习惯是将所有id内容命名为一个变量并放在代码最前面，有助于判断和排查问题。

### 5、ecode中使用国际化标签

!\[\[Pasted image 20260209160429.png\]\] 如图，在后台自定义标签中新建，代码中正常使用getLabel获取即可

### 6、event触发后，有断点到但是组件不更新

此问题特定表现：页面渲染了组件，在mount时注册了事件，外部通过点击事件等触发事件；观察到事件内部断点会执行，但是setState后不会触发组件刷新；

实际问题在标准开发中也有可能实现，由于二开模式下较多使用eventEmitter故较为常见，成因是因为注册的事件捕获了旧的状态形成闭包导致的。 解决方案可参考ahooks中useMemoizedFn，用该hook包裹需要被注册的事件即可解决。

function useMemoizedFn(fn) {
	const fnRef = useRef(fn);
	fnRef.current = useMemo(() =\> fn, \[fn\]);
	const memoizedFn = useRef();
	if (!memoizedFn.current) {
		memoizedFn.current = function (this, ...args) {
			return fnRef.current.apply(this, args);
		};
	}
	return memoizedFn.current;
}## 成都内部EB常见场景和sdk

### 接口请求

weappUtils.request### 拦截接口请求和拦截接口返回（这个后续可能会采用公司白名单）

//请求参数拦截
axios.interceptors.request.use(function (config) {
    //业务逻辑
	return config
},(error) =\> {
return Promise.reject(error);
})
//接口返回参数拦截
axios.interceptors.response.use(function (response) {
	return response
},(error) =\> {
	return Promise.reject(error);
})### 页面内组件传参

//原始方法
//使用场景
//左侧树，右侧列表。需求是根据左侧项目，联动筛选右侧列表
//https://zgjsjrdev.e-cology.cn/sp/ebddesigner/1157582145629790260
const pageSdk = ebuilderSDK.getPageSDK(pageId);
const instanceId = pageSdk.instanceId;
weappEbdcoms.setEbParams('pageParams', pageId, { instanceId, urlParams: {...urlParams } });
//封装的sdk
https://zgjsjrdev.e-cology.cn/ecode/main/item/1053765493196496898
//sdk使用
window.sjrProjectSDK.setPageCompParams('1021855703006281728', { projId: '-1' });### 组件覆写

import {
    eventEmitter
} from '@weapp/utils';

class DemoPlugin {
    // 插件唯一键，必须
    name = 'DemoPlugin\_a5c9e9243e364326ad5dd06f000c0925';
    // 具体自定义复写的API
    renderCell = (hook) =\> {
        return (opts, ...args) =\> {
            //按opts里具体的字段信息来复写内容
            if (opts.field.name === 'requestName') {
                const dueDate = dayjs(opts.data.createDateTime).format('MM-DD')
                return ( \<
                    div style = {
                        {
                            display: 'flex',
                            alignItems: 'center'
                        }
                    } \> {
                        hook(opts, ...args)
                    } \<
                    div style = {
                        {
                            color: '#ed4e4e',
                            padding: '0px 4px',
                            border: '1px solid #ed4e4e',
                            height: '20px',
                            lineHeight: '20px',
                            borderRadius: '4px',
                            marginLeft: '8px'
                        }
                    } \> {
                        dueDate
                    }
                    到期 \< /div\> \<
                    /div\>
                );
            } else {
                return hook(opts, ...args)
            }

        };
    };
}

//以下三种方式按实际使用场景选择使用
// 1. 可通过组件类型针对某个类型的组件作用插件
eventEmitter.on('@weapp/designer', 'plugin\_center.ready.NList', ({
    registerPlugin
}) =\> {
    registerPlugin(DemoPlugin);
});
// 2. 通过组件id针对某个具体的组件作用插件
const comId = 'd4e3a03b823d4ecab201ef625ab17302'; /\*\* 需要复写组件id \*/

eventEmitter.on('@weapp/designer', \`plugin\_center.ready.${comId}\`, ({
    registerPlugin
}) =\> {
    registerPlugin(DemoPlugin);
});
// 3. 通过复写组件props作用插件
regOvProps(
    'weappEbdcoms',
    'NList',
    (props) =\> {
        props.extrapluginClass = \[DemoPlugin\];
        return props;
    },
    0
);### 动作流触发

//{"customParams":{"mainTable":{"parentFolder":"#必填","projType":"#必填","id":"#必填"}},"moduleSource":"#可选","esbFlowId":"1966399233527517257"}
function infoTrigger() {
    const data = {
        customParams: {
            mainTable: {},
        },
        moduleSource: "动作流描述",
        esbFlowId: "1071175641393078285", //动作流id
    };
    weappUtils
        .request({
            url: "/api/esb/server/event/triggerActionFlow", //接口固定
            method: "POST",
            data: data,
        })
        .then((res) =\> {
            if (res.resultCode === 200) {
                weappUi.Dialog.message({
                    type: "success",
                    content: "操作成功"
                });
            }
        })
}![](_assets/440700815435366400/001-0983339a0d.png)

### eb页面组件自定义开发

https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/286fb24b-cd1d-4e87-a9c5-e8e89e8092f5 ![](_assets/440700815435366400/002-3624765903.png)

### 创建接口获取建模表数据

https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/repository/185ea76a-1052-4134-82c3-4a27100558dc/doc/238391368873934848 ![](_assets/440700815435366400/003-a6780bf8bd.png)

### 组件交互使用场景代码示例

https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/9acda63a-e734-4119-a73e-c47d2df168bf ![](_assets/440700815435366400/004-4b61bd948b.png)

### 专项二开口子

#### 根据id获取项目任务配置 用于获取一些基本信息 名称、负责人、状态等

//getProjectSimpleConfig、getTaskSimpleConfig、getProjectConfig
window.sjrProjectSDK.getProjectSimpleConfig({projectId = '', projectTypeId = ''}, cb, errCb)### 问题排查

#### 二开屏蔽

Ctrl+Shift+P (Windows/Linux) / Cmd+Shift+P（mac）找到Network Request Blocking，增加 */ecode* 勾选后刷新

#### ecode不生效文档

https://weapp.eteams.cn/ecode/playground/doc/share/view/974682604024152068#7%E3%80%81%E5%89%8D%E7%AB%AF%E6%BA%90%E7%A0%81%E6%97%A0%E6%B3%95%E6%9F%A5%E7%9C%8B%E6%80%8E%E4%B9%88%E5%8A%9E%EF%BC%9F
