奥创前端是用

634 阅读4分钟

奥创前端使用介绍

如何使用

安装核心包

若要使用奥创开发页面,需安装奥创核心包,可用 yarn 或 npm 进行安装。

yarn add @aochuang/core  || npm install @aochuang/core

渲染样例

第一步:由前后端同学共同商讨,分析如何划分组件,并得出相应页面协议数据,以如下协议数据为例:

一般协议数据由 5 部分组成,分别是 linkage,hierarchy,data,endpoint,reload。

hierarchy:描述了整个页面的组成结构

root 代表该页面的根组件,一个页面的渲染,需找到页面的根组件,再根据structure结构,进行渲染。若不存在根组件,页面将无法渲染;

structure 描述了组件间的结构关系,页面也就是根据这个结构进行依次渲染;例如该协议中,组件 Child_1 表示 Item_1 的自组件。

components 涵盖了页面存在的所有组件。

linkage:对于前端,比较关注的是 linkage 中 inputs 和 requests 属性,一个组件,如果属于 inputs 类型,则该组件能够通过调用 action 里的 onChange

方法,对协议组件上的 data 数据属性进行修改,但不会触发其他组件的变化;如果属于 requests 类型,则该组件不仅能调用 action 里的 onChange 方法,还会触发

ajax 请求,通常请求地址为 linkage 下的 url,并带上触发请求组件的 data,所有 inputs 下的 data 数据,以及 operator(触发 onchange 的组件的 uniqueId),hierarchy 和 linkage 下的所有数据。

// 页面变化触发的请求样例,(框架层会处理)

  postRequest(url, {

          operator: props.data.uniqueId, //触发 onchange 的组件的 uniqueId

          data: JSON.stringify(submitData), //触发请求组件的 data和所有 inputs 下的 data 数据

          linkage: JSON.stringify(currProtocolData.linkage),

          hierarchy: JSON.stringify(currProtocolData.hierarchy),

        })

后台将返回最新的协议数据,以此达到修改其他组件协议数据的功能,常用于当你想触发某个组件的改变,引起其他组件的变化的请况。

注意: 一个组件不能既是 inputs 类型,又是 requests 类型。

data:各个组件的标识数据,通常有些固定属性,$class 对应后台 java 类指向。uniquedId 表示该组件 data 的唯一标识,通常由 tag + id 构成,parentTag + parentId 构成父组件的唯一标识。

一般组件的业务数据属性都放在 prop 下。

endpoint:渲染端,表示在 pc 端渲染还是在移动端渲染。

reload:是否重新加载。为 true 时,调用 adjust 接口,后端返回的协议数据将会全部替换原来的协议数据,为 false 时,协议只替换后端返回的 data 数据,以及 linkage 部分。

// 协议数据

const protocolData = {

  linkage: {

    inputs: ["Child_1"],

    requests: ["Item_1"],

    url:''

  },

  hierarchy: {

    root: "Item_1",

    structure: {

      Item_1: ["Child_1"],

    },

    components:[

      "Item_1",

      "Child_1"

    ]

  },

  data: {

    Item_1: {

      uniqueId: "Item_1",

      $class: "Item_1",

      id: '1',

      tag: 'item'

      parentTag:null,

      parentId:null,

      prop: {

        name: "组件 A",

        count: 111,

      },

    },

    Child_1: {

      uniqueId: "Child_1",

      $class: "Child_1",

      id: '1',

      tag: 'child'

      parentTag:'item',

      parentId:'1',

      prop: {

        name: "组件 B",

      },

    },

  },

  endpoint: { type: 'PC' },

  reload: false,

};

第二步: 将协议数据里的组件对应成前端 react 组件,可以根据自己的业务需求进行实现,并注册到奥创核心,交由奥创核心调度渲染。

如下,定义了组件 A,组件 B,并分别将其注册到奥创核心

import React, { memo, useEffect } from "react";

import { UIFunctionComponent } from "@aochuang/core";



function Aco(props: IAochuangComponent) {

  return (

    <div>

      {props.data.prop.name || "未定义"}

      {props.children} //当组件下还有子组件的情况下,必须在此进行声明

    </div>

  );

}



function Bco(props: IAochuangComponent) {

  return (

    <div>

      {props.data.prop.name || "未定义"}

    </div>);

}



UIFunctionComponent(Aco, "item");  // 将组件A注册到奥创核心,注意:组件注册名不能带有下划线

UIFunctionComponent(Bco, "child"); // 将组件B注册到奥创核心



export default memo(Aco);

如果你使用的是类组件,那么可以使用装饰器的方式进行注册。

import React, { memo, useEffect,Component } from "react";

import { UIComponent } from "@aochuang/core";



// 将组件A注册到奥创核心 注意:组件注册名不能带有下划线

@UIComponent("item")

class Aco extends Component<IAochuangComponent> {



  render() {

    const {data, children} = this.props;

    const {name} = data.prop || {};

    return (

      <div>

        {data.prop.name || "未定义"}

      </div>

    );

  }

}



export default memo(Aco);

第三步:创建渲染入口组件,并将之前写好的页面组件加载进来,根据对应的协议数据,即可完成最基本的渲染。

import { useEffect, useState } from "react";

import { Starter } from "@aochuang/core";

import { render } from "react-dom";

import { cloneDeep, set } from "lodash";

// 引入第一步中的协议数据,真实应用中,协议数据应该是调用接口所得

import protocolData from './protocolData'



export default function Test() {

  const [show, setShow] = useState(false);

  useEffect(() => {

    (async () => {

      await import("./Aco"); // 引入并加载组件,需等组件全部加载完成,奥创组件方可正常渲染

      setShow(true);

    })();

  }, []);



  return (

    <div>

      // 奥创组件渲染

      {show ? <Starter data={protocolData} /> : null}

    </div>

  );

}

render(<Test />, document.getElementById("root"));

核心接口

//奥创监听器接口定义

interface UltronListener {

  add(name: string, fn: Function): () => void;



  remove(name: string, indicator?: string): void;



  emit(name: string, ...data: any);



  events: Record<string, string>;

}



// 奥创action接口定义

interface IAochuangComponentAction {

  onChange: (key: string | Record<string, any>, value: any) => void;

  onSubmit: (url: string) => Promise<any>;

  listener: UltronListener;

}



// 奥创linkage接口定义

interface ILinkage {

  inputs: string[];

  requests: string[];

  url: string;

}



// 奥创hierarchy接口定义

interface IHierarchy {

  root: string;

  components: string[];

  structure: Record<string, string[]>;

}



//

interface IEndPoint {}



// 奥创协议接口定义

interface IAochuangProtocol {

  linkage: ILinkage;

  hierarchy: IHierarchy;

  endpoint: IEndPoint;

  data: Record<string, IBizComponentData>;

  reload?: boolean; //reload为true时,调用adjust接口会替换整个协议数据

}



// 奥创组件列表类型定义

type IBizComponents = string[];



// 奥创组件data接口定义

interface IBizComponentData<prop = any> {

  $class: string; // 对应后台Java类

  uniqueId: string; // 组件数据唯一id

  id: string;  // 实例id

  tag: string; // 注册的组件类型 id + tag = uniqueId

  parentTag?: string | null; // 父级组件类型

  parentId?: string | null;  // 父级实例id

  prop?: prop; // 业务属性

}



// 奥创组件props接口定义

interface IAochuangComponent<T = IBizComponentData> {

  action: IAochuangComponentAction;

  data: T;

  children?: any; // 子组件列表 string[]

  childrenData: T[]; // 子组件数据列表

  extraPropData?: any; // 额外扩展prop属性

}

常用方法

当常规的奥创主动调用渲染不能满足实际应用开发需求的情况下,奥创核心还提供以下几个常用的方法,来帮助开发者更好的完成组件的自定义渲染。

1.getChildren(uniqueId: string)

根据组件 uniqueId,获取子组件列表,例如当你需要获取特定子组件,并分别对其进行渲染时,可以根据组件的 uniqueId,获取子组件的 uniqueId 列表,进行进一步的处理。一般

会与 getData,getDom,renderItem 配合使用。

2.getData(uniqueId: string)

根据组件 uniqueId,获取组件 data 数据,当你只需要获取组件的数据属性,并不对组件进行实现的时候,使用该方法进行获取。

例如,现在有一个 table 组件,下面挂着 columnList 组件,columnList 下又是各个列属性组件,此时若我不对列属性组件进行单独实现,只需获取列属性组件里的相关数据即可。

具体代码参考如下:

const getColumns = () => {

    const columns = flatten(

      props.childrenData

        .filter((dataItem: IBizComponentData) => dataItem.tag === 'columnList')

        .map((i: any) => {

          const children = renderInstance.getChildren(i.uniqueId);

          return children.map((uniqueId: string) =>

            renderInstance.getData(uniqueId)

          );

        })

    );

    return columns.map((i: IBizComponentData) => {

      let key = [];

      if (i.prop.key.indexOf('.') !== -1) {

        key = i.prop.key.split('.');

      } else {

        key = i.prop.key;

      }



      return {

        dataIndex: key,

        title: i.prop.name,

      };

    });

  };

3.renderItem(Comp: React.ComponentType,uniqueId: string,extraData?: any)

主动渲染奥创组件的方法,与 getDom 类似,区别在于传入的 Comp 组件未注册到奥创核心,通常用于 Tabs 与 TabPanel 这种强关联组件,TabPanel 无法单独注册到奥创核心。当 TabPanel 还存在子组件,

则可用到 renderItem 的写法,主动渲染未注册到奥创核心的 TabPanel 组件。示例如下:

import React, { useEffect } from 'react';

import { Tabs } from 'antd';

// @ts-ignore

import { UIFunctionComponent, renderInstance } from '@aochuang/core';

import Style from './index.less';



const TaskTabList = (props: IAochuangComponent) => {

  const { currentSelectedTabId } = props.data.prop || {};



  const tabOnChange = (activeKey: string) => {

    props.action.onChange('currentSelectedTabId', activeKey);

  };



  useEffect(() => {

    if (!currentSelectedTabId) {

      console.log('发起请求', props.childrenData[0].prop.id);

      props.action.onChange(

        'currentSelectedTabId',

        props.childrenData[0].prop.id

      );

    }

  }, []);

  return (

    <Tabs

      onChange={tabOnChange}

      activeKey={currentSelectedTabId}

      className={Style['tab-active']}

    >

      {props.childrenData.map((i: IBizComponentData, index: number) => (

        <Tabs.TabPane key={i.prop.id} tab={i.prop.title}>

          {renderInstance.renderItem(TaskTab, i.uniqueId)}

        </Tabs.TabPane>

      ))}

    </Tabs>

  );

};

const TaskTab = (props: IAochuangComponent) => <>{props.children}</>;



UIFunctionComponent(TaskTabList, 'TaskTabList');

export default TaskTabList;

4.getDom(uniqueId: string, propData?: any)

可根据组件 uniqueId 主动渲染奥创组件,并且何为组件透传额外的 props 数据:propData。

还是举 table 组件为例,当我需要既需要列属性组件里的数据,有需要针对某列进行特殊展示时,如标签,进度条的展示,这时不得不对列属性组件进行实现,并通过组件

uniqueId 主动进行渲染,代码如下所示:

const getColumns = () => {

    const columns = flatten(

      props.childrenData

        .filter((dataItem: IBizComponentData) => dataItem.tag === 'columnList')

        .map((i: any) => {

          const children = renderInstance.getChildren(i.uniqueId);

          return children.map((uniqueId: string) =>

            renderInstance.getData(uniqueId)

          );

        })

    );

    return columns.map((i: IBizComponentData) => {

      let key = [];

      if (i.prop.key.indexOf('.') !== -1) {

        key = i.prop.key.split('.');

      } else {

        key = i.prop.key;

      }



      return {

        dataIndex: key,

        title: i.prop.name,

        render: (text: any, record: any, index: number) =>

          renderInstance.getDom(i.uniqueId, { text, record, index }),

      };

    });

  };