国产91在线无码_少妇人妻无码高清_91人妻中文字幕无码专区在线_国产福利在线播放_免费 无码 国产成年视频网站

阿里低代碼框架 lowcode-engine 低代碼表單實戰(zhàn)(阿里低代碼平臺)

前沿

lowcode-engine功能比較強(qiáng)大,最近這段時間做了個低代碼表單的實戰(zhàn),在過程中遇到一些問題,在這里做下介紹和總結(jié)。

功能演示

阿里低代碼框架 lowcode-engine 低代碼表單實戰(zhàn)(阿里低代碼平臺)

前臺功能

主要介紹一下前臺功能的基本實現(xiàn)和一些問題。

FormContainer容器組件

我們的默認(rèn)容器不是頁面,而是需要自定義容器。例如,在常見的低代碼平臺中默認(rèn)容器是表單容器,通過表單容器類提供布局能力。這塊之前有一篇文章詳情介紹,可以查看FormContainer容器。

那篇文章介紹了怎么實現(xiàn)自定義容器,我們打開詳情頁面,看到所有的表單項都是只讀的,我們在容器中做一個全局狀態(tài)管理,在這里用context去實現(xiàn)。

  • 定義 Provider

// 定義FormContainerProviderexport const FormContainerProvider: FC<IFormContainerProviderProps> = ({ children, isMobile }) => { const processorAction = useCreation(() => { return createFormContainerProcessor(); }, []); const { processor, getRoot, destroy } = processorAction || {}; useEffect(() => { processor.setMobile(isMobile); }, [isMobile]); useEffect(() => { return () => { destroy?.(); }; }, []); return <Context.Provider value={processor!}>{children}</Context.Provider>;};

  • 之后我們就可以在容器組件和FormItem組件內(nèi)獲取數(shù)據(jù),這塊簡單做了封裝處理。

// 從conext獲取更改只讀的方法const [changeReadonly] = useFormContainerSelector((s) => [s.changeReadonly]);

  • Form容器對外提供能力

我們提交保存操作沒有在容器內(nèi)實現(xiàn)對應(yīng)的物料,是在外部自定義的,這時候就需要我們對FormContainer綁定Ref,之后我們獲取實例可以拿到對應(yīng)的方法。

// 綁定refReact.useImperativeHandle( ref, () => { return { formRef: form, changeReadonly, // 更改只讀方法 }; }, []);

物料組件

我們對每個表單項開發(fā)對應(yīng)的物料,物料的開發(fā),官方提供腳手架快速創(chuàng)建項目,之前也寫過一遍文章,流程不清楚的請移步自定義物料篇。這里我們用日期物料做說明,還會介紹一下開發(fā)調(diào)試,之前文章說我們要把物料發(fā)布到npm上,這樣開發(fā)調(diào)試很不方便。

Filed Date 物料

  • 定義Date物料類型

可以看到我們有個基礎(chǔ)的類型,是一些通用的屬性,columnConfig這個屬性是每個FormItem的config。

export interface IColumnEntity<T extends EFieldType = EFieldType> extends IBaseEntity { ... // 數(shù)據(jù)庫字段類型 fieldType: TFieldType; // 標(biāo)題 title?: string; // 擴(kuò)展參數(shù) extraParam?: Record<string, any>; // 列配置信息 columnConfig: T extends keyof TColumnConfigMap ? TColumnConfigMap[T] : TColumnConfig; // 校驗信息 validateConfig: IColumnValidateConfig;}

  • FieldData config

日期物料的config信息,有了具體的TS類型,在我們寫代碼的時候會事半功倍

/** * 日期 */export interface IColumnDateConfig { /** * 描述 */ description: string; /** * 占位符 */ placeholder?: string; /** * 1. 普通 2禁用 3 只讀 */ status: number; /** * 格式化類型 1. YY-MM 2. YYYY-MM-DD 3. YYYY-MM-DD HH:MM 4. YYYY-MM-DD HH:MM:SS */ format: number; /** * 默認(rèn)值類型 */ defaultValueType: string; /** * 默認(rèn)值 */ defaultValue: string;}

  • meta.ts信息

這里主要描述物料組件信息, 我們簡單介紹一下setter信息,其它的可以看官方文檔。

configure: { props: [ { title: { label: '格式', }, name: 'columnConfig.format', supportVariable: false, setter: { componentName: SelectSetter, props: { options: DateFormatConstant, changeReRenderEvent: true, }, initialValue: 2, }, }, ]}

props中的name屬性columnConfig.format,我們可以使用這種方式來描述嵌套的屬性。

  • 實現(xiàn)FieldData組件

這里相對來說也不復(fù)雜,需要注意的是porps中的內(nèi)容,有我們在meta文件中定義的props,還有FormItem中標(biāo)注的value,onChange屬性,還有一些屬性,大家可以打印下看看。有時候有些需求實現(xiàn)這上面的屬性會有幫助,

// FieldData 具體實現(xiàn)export interface IFieldDateProps extends BaseWrapperProps<EFieldType.DATE> {}export const FieldDate: FC<IFieldDateProps> = (props) => { const { columnConfig, onChange, value, ...otherProps } = props; const [readonly] = useFormContainerSelector((s) => [s.readonly]); const format = columnConfig?.format; const currFormat = DateFormatConstant.find((f) => f.value == format); const onDateChange: DatePickerProps['onChange'] = (date, dateString) => { const currUnix = date?.valueOf(); onChange?.(currUnix); }; return ( <BaseWrapper {...props}> <DatePicker style={{ width: '100%' }} disabled={readonly || columnConfig?.status === EFieldStatus.disable} placeholder={columnConfig?.placeholder} showTime={currFormat?.showTime} format={currFormat?.label || 'YYYY-MM-DD'} value={value ? dayjs(value) : undefined} onChange={onDateChange} /> </BaseWrapper> );};

setter

實現(xiàn)我們的需求,setter是一個比較重要的環(huán)節(jié),這里我們對setter做了重寫,全部使用了antd的組件。setter我們分為通用的setter和單個物料的自己的setter。

  • setter定義

官方的案例Setter使用的是字符串,也就是在引擎注入的setter供我們使用。在項目中開發(fā),我們可以用一個setter組件,待setter穩(wěn)定后,考慮引擎注入。

  • 每個setter對應(yīng)一個props屬性

上面我們在meta文件中的columnConfig.format使用了SelectSetter,定義如下:

export const SelectSetterFun: FC<ISelectSetterProps> = (props) => { const { options = [{ label: '-', value: '' }], onChange, mode, value, showSearch, onChangeEvent, changeReRenderEvent, } = props; const dataSource = formateOptions(options); const { sendReRenderEvent } = useReRenderEvent({ isBindEvent: false }); return ( <Select style={{ width: '100%' }} value={value} size={'small'} options={dataSource} onChange={(val) => { onChange?.(val); onChangeEvent?.(val); changeReRenderEvent && sendReRenderEvent(); }} showSearch={showSearch} /> );};export const SelectSetter = SetterHoc(SelectSetterFun);

  • 高階組件 SetterHoc 在setter中直接使用hooks組件會有問題,我們用類組件做一層包裹。

export const SetterHoc = (Component: any) => { return class SetterComponent extends React.Component { render() { return <Component {...this.props} />; } };};

  • 獲取和設(shè)置其它props值

有的需求我們在setter中需要獲取其它組件的屬性,通過props?.field?.parent 可以獲取到,這里封裝了一個自定的hooks,來獲取和設(shè)置值

export const usePropsValue = (props: any) => { const getPropValue = useMemoizedFn((key: string) => { const propsField = props?.field?.parent; // 獲取同級其他屬性 showJump 的值 return propsField.getPropValue(key); }); const setPropsValue = useMemoizedFn((key: string, value: any) => { const propsField = props?.field?.parent; // 獲取同級其他屬性 showJump 的值 propsField.setPropValue(key, value); }); return { getPropValue, setPropsValue, };};

還有一種方法可以可以實現(xiàn)此效果,就是在setter上設(shè)置extraProps屬性,這個屬性可以有兩個方法setValue和getValue.

  1. 在meta上設(shè)置

// 更改其它選項,在meta上設(shè)置extraProps: OptionsSetterExtraProps,

// 更改其它選項,在meta上設(shè)置extraProps: OptionsSetterExtraProps,

  • setter之間通信

在引擎中,通信需要通過事件的方式去做。在這里,通常我們有些setter的變更會影響其它setter,例如:日期的格式變化默認(rèn)值會做相應(yīng)的調(diào)整。在業(yè)務(wù)中,setter的變更,通知依賴的setter刷新,刷新的時候重新獲取屬性值,做業(yè)務(wù)調(diào)整。

在這里,封裝了reRender一個hooks,

export const useReRenderEvent = (props?: IUseReRenderEventProps) => { const { isBindEvent = true } = props || {}; const update = useUpdate(); // 強(qiáng)制觸發(fā)更新 const reRenderEvent = useMemoizedFn(() => { update(); }); /** * 發(fā)送重新渲染事件 */ const sendReRenderEvent = useMemoizedFn(() => { event.emit(EFiledEventName.ReRenderEmit); }); useEffect(() => { isBindEvent && event.on(EFiledEventName.ReRender, reRenderEvent); return () => { isBindEvent && event.off(EFiledEventName.ReRender, reRenderEvent); }; }, [isBindEvent]); return { sendReRenderEvent, };};

這個hooks,有兩個作用,一個是發(fā)送重新渲染事件,一個是監(jiān)聽渲染事件。在上面的案例當(dāng)中,

1.在格式的setter中引入該hooks,做事件發(fā)送。

const { sendReRenderEvent } = useReRenderEvent({ isBindEvent: false });return ( <Select ... onChange={(val) => { changeReRenderEvent && sendReRenderEvent(); }} ... />);

  1. 在默認(rèn)值setter中,做事件的監(jiān)聽。

// 監(jiān)聽格式的變化useReRenderEvent();// 獲取格式數(shù)據(jù)const { getPropValue } = usePropsValue(otherProps);const format = getPropValue('format');

渲染詳情頁

封裝FormContainerRnder組件,來做渲染。

  • 引擎提供了ReactRender的能力,我們傳入對應(yīng)的scheam信息,就可以做到顯示。

<ReactRenderer className="lowcode-plugin-sample-preview-content" schema={schema} designMode="dialog" rendererName="LowCodeRenderer" components={components} onCompGetRef={onCompGetRef} appHelper={{ requestHandlersMap: { Fetch: createFetchHandler(), }, }}/>

  • 獲取FormContainer組件Ref

在數(shù)據(jù)提交的時候,我們需要獲取組件的實力,在引擎中獲取Ref方法,要使用 onCompGetRef方法。

const onCompGetRef = (schema: any, ref: any) => { if ('FormContainer' === schema.componentName) { const { formRef, ...otherRef } = ref; formInstanceRef.current = ref.formRef; formOtherRef.current = otherRef; // 獲取到ref,執(zhí)行resolve promiseRef?.resolve(true); }};

在渲染的時候,我們有可能獲取不到實例,我們用個異步來處理。

// 此處異步是因為不能立馬獲取到form的實例const promiseRef = useCreation(() => { return createPromiseWrapper();}, []);

提供對外的數(shù)據(jù)能力

React.useImperativeHandle( ref, () => { return { getFormInstance: async () => { await promiseRef.promise; return formInstanceRef.current! as FormInstance; }, changeReadonly: async (disabled: boolean) => { await promiseRef.promise; formOtherRef.current?.changeReadonly?.(disabled); }, }; }, []);

  • 初始化數(shù)據(jù)

打開編輯詳情頁的時候,需要把從接口獲取的數(shù)據(jù)給設(shè)置到表單上。有了FormContainer實例,我們可以很方便的做設(shè)置

useAsyncEffect(async () => { if (mode !== EMode.create && !itemData.loading && Object.keys(itemData.data).length > 0) { // 獲取實例 const formInstance = await formRef.current?.getFormInstance(); // 數(shù)據(jù)轉(zhuǎn)換 const formValues = convertItemDataToFormValues(itemData.data, table.data.columns); // 設(shè)置值 formInstance?.setFieldsValue?.(formValues); }}, [itemData.data, itemData.loading]);

提交數(shù)據(jù)

獲取表單數(shù)據(jù),做提交。這里通過FormContainer的時候,可以獲取所有的值,包括做一些前端的校驗等。

  • 獲取所有值,調(diào)用api,做數(shù)據(jù)提交

export const getFormValues = async (formRef: React.MutableRefObject<IPreviewRef>) => { const formInstance = await formRef?.current?.getFormInstance(); return formInstance?.getFieldsValue();};

開發(fā)調(diào)試

開發(fā)物料后,如果我們發(fā)布npm,整個流程會很繁瑣,效率低,物料腳手架也提供了調(diào)試,不過在我們實際業(yè)務(wù)開發(fā)中,會有一些業(yè)務(wù)數(shù)據(jù)和上下文的環(huán)節(jié)依賴,所有要能實時調(diào)試開發(fā)。接下來幾個步驟介紹一下

  • 啟動lowcode開發(fā)模式

"lowcode:dev": "build-scripts start --config ./build.lowcode.js",

會開啟一個實時的監(jiān)聽服務(wù)。

  • 在我們引擎中的assets.json修改,使用上面服務(wù)的地址,修改內(nèi)容如下

阿里低代碼框架 lowcode-engine 低代碼表單實戰(zhàn)(阿里低代碼平臺)

修改在url中的內(nèi)容為本地地址,這時候我們開發(fā)后。刷新瀏覽器,會實時看到結(jié)果

  • 環(huán)境變量,動態(tài)切換

import assetsLocal from '../services/assets-local.json';import assets from '../services/assets.json';export const getAssetsJson = () => { // 用本地配置文件 if (process.env.LOCAL_UI_MATERIAL === 'true') { return assetsLocal; } return assets;};

總結(jié)

以上就是對lowcode-engine低代碼實戰(zhàn)內(nèi)容,后續(xù)我們介紹一下引擎和后臺之間的交互,可以讓大家實現(xiàn)一個完整的案例。

作者:Witty_Wizard
鏈接:https://juejin.cn/post/7346865556328808463

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實,本站將立刻刪除。

(0)
上一篇 2024年7月8日 下午6:47
下一篇 2024年7月8日 下午6:58

相關(guān)推薦