组件包目录结构

下图展示了一个组件包的目录及文件结构。组件包下载到 IDE 后,目录发生变化,前端代码在 UI2/comp 目录下,服务端代码在 service/comp 目录下

前端目录

组件前端目录包括组件目录 components 和配置目录 config

components 目录

components 目录包括组件命名空间目录(和组件包同名)、若干组件目录和组件注册文件。

组件命名空间目录

组件命名空间目录与组件包目录同名,基础通用逻辑可以写到这个目录下,组件依赖的第三方库放到 lib 目录下

组件目录

一个组件包括设计时文件、编译时文件(非必须)和运行时文件

│  ├─组件目录 //组件名
│  │  ├─css     //运行时css目录
│  │  │     组件名.css //运行时样式
│  │  │
│  │  │  组件名.js.xdoc //组件的xdoc说明文档
│  │  │  组件名.meta.json //组件元信息文档
│  │  │  组件名.react.js //react 体系组件运行时 js
│  │  │  组件名.vue  //vue 体系组件运行时 js
│  │  │  
│  │  ├─designer    //组件设计时目录
│  │  │  │  组件名.js   //组件设计器js
│  │  │  │  组件名.react.js //React 体系组件设计时 js
│  │  │  │  组件名.vue //Vue 体系组件设计时 js
│  │  │  │  组件名.xml //组件模版
│  │  │  │  
│  │  │  ├─css //设计时css路径
│  │  │  │      组件名.css //设计时样式
│  │  │  │  
│  │  │  └─img //图片路径
│  │  │          图片文件 //组件图标
│  │  │      
│  │  └─server
│  │      │  model.config.xml //指定依赖 class 的路径
│  │      │  
│  │      └─dsrc
│  │              组件名.java //组件编译时 java 文件
运行时文件

运行时文件包括组件元信息文件、运行时 JS 文件、CSS 文件、xdoc文件

组件元信息文件

组件元信息文件定义了组件的属性、操作、事件、工具栏、引用等信息

详细说明参考《组件元信息文件

运行时 JS 文件

组件运行时 JS 文件用于在运行的界面上显示组件,完成和用户的交互

  • React 体系的运行时 JS 文件的文件名为:组件名.react.js
  • Vue 体系的运行时 JS 文件的文件名为:组件名.vue

详细说明参考《组件运行时 JS 文件

运行时 CSS 目录

存放组件运行时 CSS 文件,CSS 文件内定义组件运行时的样式,需在运行时 JS 文件中引入。 CSS 文件名为:组件名.css

  • React 体系中,组件名.react.js 中引用 CSS 文件代码如下
import "./css/组件名.css";
  • Vue 体系中,组件名.vue 中引用 CSS 文件代码如下
<style src="./css/组件名.css" />

下面是“描述列表”组件的运行时 CSS 文件,其中的代码用于设置“描述列表项”中的“日期选择”组件不显示后缀图标

.ant-description-item-form-item-control{
    /**
    日期选择
     */
    .ant-picker-suffix {
        visibility: hidden;
    }
}

运行效果如下图所示

在 IDE 中切换到代码页,查看“描述列表”组件的完整代码

antdpro组件包:$UI/comp/antdpro/components/Descriptions
antdv组件包:$UI/comp/antdv/components/Descriptions
组件注释文件

组件注释文件的文件名为组件名.js.xdoc。该文件简要描述组件使用场景、使用方法、操作、方法等内容

  • 使用场景和使用方法会显示在组件面板中,当鼠标悬停在该组件的时候以气泡方式弹出显示
  • 组件提供了操作、方法时,需在此文件中声明组件的操作、方法等信息,用于模型检查

xdoc 文件中有一个组件描述,若干个操作、方法说明。

  • 组件描述

组件描述代码如下,其中 \ 中的内容是组件面板中显示的信息,@component 描述这一段注释是一个组件描述

/**
    @name $UI/comp/命名空间/components/组件名/组件名
    @label 组件中文名
    @extends $UI/comp/命名空间/components/命名空间/命名空间Component
    @component 
    @description
    <xmp>
         <b>使用场景</b>:描述组件使用场景
         <b>使用方法</b>:描述组件使用方法
    </xmp> 
    @model UI2
*/

下面是“抽屉”组件的 xdoc 文件

/**
    @name $UI/comp/antdpro/components/Drawer/Drawer
    @label 抽屉
    @extends $UI/comp/antdpro/components/antdpro/AntdproComponent
    @component 
    @description
    <xmp>
         <b>使用场景</b>:屏幕边缘滑出的浮层面板。
         <b>使用方法</b>:可以配置抽屉的位置,点击“显示”进行内容的设计。
    </xmp> 
    @model UI2
*/

在 IDE 的组件面板中的运行效果,如下图所示

  • 组件方法说明

组件方法说明代码如下,其中 @function 描述这一段注释是一个方法说明

/**
    @name $UI/comp/命名空间/components/组件名/组件名#方法名
    @label 组件方法中文名
    @extends $UI/comp/命名空间/components/命名空间/命名空间Component
    @function
    @description 方法描述
    @model UI2
*/

下面是“抽屉”组件的“显示”方法的说明代码:

/**
      @name $UI/comp/antdpro/components/Drawer/Drawer#show
      @priority 30
      @label 显示
      @function
      @description 显示
      @returns {void}
*/
  • 组件操作说明

组件操作说明代码如下,其中 @operation 描述这一段注释是一个操作说明

/**
    @name $UI/comp/命名空间/components/组件名/组件名#操作名
    @label 组件操作中文名
    @extends $UI/comp/命名空间/components/命名空间/命名空间Component
    @operation
    @description 操作描述
    @model UI2
*/

下面是“抽屉”组件的“显示”操作的说明代码:

/**
      @name $UI/comp/antdpro/components/Drawer/Drawer#show
      @priority 30
      @label 显示
      @operation
      @description 显示
      @returns {void}
*/
设计时文件

设计时文件位于组件目录/designer 目录下,包括组件描述文件,设计器 JS 文件,设计时 JS 文件,设计时 CSS 文件

组件描述文件

designer/组件.xml。此文件中描述组件的中文名、英文名、辨识条件、父容器、组件的模板等内容。

  • name:命名空间:组件名
  • tag-name:组件名
  • text:组件中文名
  • discriminate-condition:区别其他组件的唯一条件
  • parent-range:双击该组件添加到指定父容器,不指定时,默认添加页面选中的组件里。 格式为:命名空间:组件名,命名空间:组件名,,也可以通过“ $UI/wxsys/comps/inVisibleCompContainer/inVisibleCompContainer ” 加到不可视区域中。
  • icon-type:指定为 icon 时,表示组件使用字体图标
  • icon:组件图标,可以是文件名(文件存放于 designer/img 下)也可以是字体图标的 class,字体图标 class 来源于 $UI/wxsys/comps/icon/css/fonts/common/icons.css
  • component-type:可选值为 inVisibleComp|formControl 。
  • hasView:标记组件是否有视图,可选值为true|false(使用该属性已知的组件:wx:importExcel、wx:exportExcel 、wx:print)。
    • 扩展说明:vue 架构的页面中,有 .js 文件和 data.js,有视图的组件对应的事件写入 .js 中,无视图的组件对应事件写入 data.js 中
    • 目前无视图的组件分为如下三类
      • wx:model下的所有组件
      • 组件描述文件中有 hasView="false"属性
      • data系列组件(wx:restData| wx:tableData| wx:tableCustomData| wx:jsonData| wx:serviceData| wx:aggregateData ),因部分 data 组件没在 wx:model 下,固此处单独列出
  • events 节点描述的是标准 W3C 事件。组件事件描述在元信息文件中。

templates 节点中的内容描述的是组件默认的 xml 模型。

<elements>
    <element name="命名空间:组件名" tag-name="组件名"
        text="中文名"
        discriminate-condition="辨识条件"
        parent-range="指定父容器"
        icon-type="icon" 
        icon="icon icon-android-storage" component-type="inVisibleComp"
>
        <!--通用事件-->
        <events>
            <include path="$UI/wxsys/comps/reactContainer/commonConfig.xml#//react-events-pcx/*" />
        </events>

        <templates>
            <template name="default"><![CDATA[
                <!--添加到页面的模版内容-->
           ]]></template>
        </templates>
    </element>
</elements>

例如:“抽屉组件”的 xml 。

<?xml version="1.0" encoding="UTF-8"?>
<elements>
    <element name="antdpro:Drawer" tag-name="Drawer"
        text="抽屉"
        discriminate-condition="executeXpath(name()='antdpro:Drawer')"
        parent-range="$UI/wxsys/comps/inVisibleCompContainer/inVisibleCompContainer"
        icon-type="icon" 
        icon="icon icon-android-storage" component-type="inVisibleComp"
        design-view="web-designer" d_resiable="false" resizable="false">
        <events>
            <include path="$UI/wxsys/comps/reactContainer/commonConfig.xml#//react-events-pcx/*" />
        </events>

        <templates>
            <template name="default"><![CDATA[
                <antdpro:Drawer xmlns:antdpro="$UI/comp/antdpro/components" xmlns:attr="http://www.wex5.com/attr" >
                    <attr:title class="title" d_canAddChild="true" designer_label="标题" >
                        <span>标题</span>
                    </attr:title>
                </antdpro:Drawer> 
           ]]></template>
        </templates>
    </element>
</elements>
  • designer/img 存放组件图片的路径
设计器 JS 文件

设计器 JS 文件名为组件名.js。该文件使用 AMD 规范,结合组件元信息文件完成设计器的渲染和操作逻辑。引用组件元信息文件、设计时 JS 和 CSS 文件。在组件元信息文件中提到的方法,在本文件中定义。

define(function(require) {
    //引用命名空间设计时基类 antdproComponent
    var AntdproComponent = require("../../命名空间/designer/命名空间Component");

    //React 体系引用 reactLibrary 下的 componentsConfig
    var componentsConfig = require("babel!$UI/wxsys/comps/reactContainer/designer/reactLibrary/componentsConfig");

    //Vue 体系引用 vueLibrary 下的 componentsConfig
    var componentsConfig = require("babel!$UI/wxsys/comps/reactContainer/designer/reactLibrary/componentsConfig");

    //引用组件的元信息文件
    var 组件名Meta = require("text!../组件名.meta.json");
    componentsConfig.defineMeta(组件名Meta);

    //引用设计时工具类,读取元信息文件
    var designerUtils = require("babel!$UI/wxsys/comps/reactContainer/designer/designerUtils");

    //子元素被单独取属性后渲染,子元素只作为属性提供(非必须),如:Table、Select、CellGroup、Tabs、Form 等
    componentsConfig.changeChildrenLocationComponent.push("命名空间:组件名");

    //组件不会渲染或渲染不对,需要依赖父渲染(非必须),如 From.Item、Table.Column、Select.Option
    componentsConfig.dependParentReRenderComponent.push("命名空间:组件名");

    //引用组件设计时 CSS 文件(非必须)
    require("css!./css/Drawer").load();

    //引用组件设计时 JS 文件(非必须)
    命名空间BaseComponent["组件名"] = require("babel!./组件名.react");

    var 组件名 = 命名空间Component.extend({
        init: function (value, bindingContext) {
            //组件初始化
            //获取xml模型
            this.ownerDesigner.dataModel.getModelElement($(this.domNode).attr("d_id"))
        },
        getDesignDefaultProps: function (instance) {
            //用户设置的属性会覆盖在此设置的属性,初始值时一个空对象
        },
        getDesignProps: function (instance) {
            //在此设置的属性会覆盖用户设置的属性,初始值时上次渲染设置的props对象
        },
        propertyChangedHandler : function(key, oldVal, value) {
            //用户设置属性发生改变时的回调
        },
        eventChangedHandler : function(key, oldVal, value) {
            //用户设置事件发生改变时的回调
        },
        reRender : function() {
            //重绘
        },
        attrToolbarVisibleCondition : function (contextInfo) {
            //在此可以设置工具栏上的按钮的显示隐藏。搭配元信息文件中的 enable-condition 或 visible-condition 使用。
        },
        // 元信息文件中 toolbar 的自定义方法在此定义
        ....
    });

    var ideConfig = {
        "命名空间:组件名": designerUtils.initIdeConfig(组件名, 组件名Meta)
    }

    return {
        "命名空间:组件名": 组件名,
        webIdeEx: ideConfig,
        quickIdeEx: ideConfig
    }
})

下面是“加载中”组件的设计器 JS 文件,引用了元信息文件和样式文件,代码如下

define(function(require){
    var AntdproComponent = require("../../antdpro/designer/AntdproComponent");
    var componentsConfig = require("babel!$UI/wxsys/comps/reactContainer/designer/reactLibrary/componentsConfig");
    var SpinMeta = require("text!../Spin.meta.json");
    componentsConfig.defineMeta(SpinMeta);
    var designerUtils = require("babel!$UI/wxsys/comps/reactContainer/designer/designerUtils");
    require("css!./css/Spin").load();

    var Spin = AntdproComponent.extend({
    });

    var ideConfig = {
        "antdpro:Spin": designerUtils.initIdeConfig(Spin, SpinMeta)
    }

    return {
        "antdpro:Spin": Spin,
        webIdeEx: ideConfig,
        quickIdeEx: ideConfig,
    };
});

在“弹出层”组件中定义了工具栏按钮,代码如下

{
    "Modal": {
        "toolbar": [
            {
                "text": "显示",
                "method": "show"
            }
        ]
    }
}

“弹出层”组件的设计器 JS 文件中定义了工具栏按钮使用的 show 方法,代码如下

define(function(require) {
    var Modal = AntdproComponent.extend({
        show: function () {
            this.changeVisible(true);
        },
        changeVisible: function (flag) {
            var self = this;
            this.setState({ open: flag }, function () {
                self.reRender();
            });
        }
    });

    var ideConfig = {
        "antdpro:Modal": designerUtils.initIdeConfig(Modal, ModalMeta)
    }

    return {
        "antdpro:Modal": Modal,
        webIdeEx: ideConfig,
        quickIdeEx: ideConfig
    }
})
设计时 JS 文件

组件设计时 JS 文件用于在 IDE 的设计界面上显示组件,完成和开发者的交互

  • React 体系的设计时 JS 文件的文件名为:组件名.react.js
  • Vue 体系的设计时 JS 文件的文件名为:组件名.vue

详细说明参考《组件设计时 JS 文件

设计时 CSS 目录

存放组件设计时 CSS 文件,CSS 文件内定义组件设计时的样式,需在设计器 JS 文件中引入。 CSS 文件名为:组件名.css

下面是“卡片”组件的设计时 CSS 文件,其中的代码用于选择卡片的扩展区

[componentname="antdpro:Card"] {
  display: block;
}

/**设置卡片的扩展区大小,以便用户在设计器上可以选择该扩展区 */
[componentname="attr:extra"] {
  min-width: 48px;
  min-height: 24px;
  display: inline-block;
}
编译时文件

编译时文件是一个动态 java 文件,位于 server/dsrc/组件名.java。

页面的运行时文件,是通过 Java dom4j 从 .w 模型文件编译生成的。通过定义编译时 java 文件,可以增加或减少组件的属性。平台底层已封装了通用的编译逻辑,仅当通用逻辑无法满足用户需求的时候,才需编写该文件来完成页面编译,绝大多数场景不用写。

例如:“表单项组件”,要从“表单项”里获取到“输入框”等表单组件上的 bind:ref 信息来完成表单项上显示校验提示信息的能力,就需要编写此文件。

public class FormItem extends AntdproComponent{
    //如果没有bind:ref, validateInfo找子节点中的bind:ref, 用来显示错误信息
    /**
    @param bound 组件xml模型
     */
    public void beforeDoExeucte(Element bound, JSONObject props, Map<String, Object> context, WXComponentContext wxContext){
        if (!hasRef(bound)){
            String ref = getRefFromChildren(bound);
            if (SystemUtils.isNotEmptyString(ref)) {
                bound.addAttribute(WXUtils.BIND_REF, ref);
            }
        }
        super.beforeDoExeucte(bound, props, context, wxContext);
    }

    private String getRefFromChildren(Element e) {
        if (e != null) {
            String[] attrs = new String[] {"ref", "beginref", "endref", "beginRef", "endRef"};
            for (String attr : attrs) {
                String value = e.attributeValue(new QName(attr, WXUtils.BIND_NAMESPACE));
                if (SystemUtils.isNotEmptyString(value)) {
                    return value;
                }
            }
        }
        List children = e.elements();
        if (children != null) {
            for (Object child : children) {
                Element c = (Element) child;
                String bindItemValue = c.attributeValue(new QName("items", WXUtils.BIND_NAMESPACE));
                if (SystemUtils.isNotEmptyString(bindItemValue)) {
                    continue;
                }
                String value = getRefFromChildren((Element)child);
                if (SystemUtils.isNotEmptyString(value)) {
                    return value;
                }
            }
        }
        return null;
    }
}

例如:“日期范围选择”组件的“开始时间”和“结束时间”属性。

{
    "DatePicker.RangePicker": {
        "properties": {
            "bind:beginRef": {
                "type": "dataRef",
                "label": "开始时间",
                "required": "true"
            },
            "bind:endRef": {
                "type": "dataRef",
                "label": "结束时间",
                "required": "true"
            }
        }
    }
}

public class DatePickerRangePicker extends AntdproComponent {

    public void processRef(Element bound, String ref, String prefix, Map<String, Object> context, WXComponentContext wxContext) {
        // 处理bind:ref相关逻辑
        String refValue = attributeValueAndRemoveAttr(bound, new QName(ref, WXUtils.BIND_NAMESPACE));

        String dataId = this.getDataIdExprByRef(refValue, context, wxContext);
        bound.addAttribute(prefix + "RefDataId", dataId);

        String columnName = this.getColumnByRef(refValue);
        bound.addAttribute(prefix + "RefColumnName", columnName);

        String row = this.getRowByRef(refValue);

        row = DataBindUtils.parseDataBind("{{" + row + "}}",
                (Set<String>) this.context.get(ComponentContext.RENDER_VARS), new Stack<String>(),
                DataBindUtils.ATTR_BIND, "", new ArrayList<String>(), new ArrayList<String>(), false);
        bound.addAttribute(prefix + "RefRow", row);

    }

    public JSONObject doExecute(Element bound, Map<String, Object> context, WXComponentContext wxContext) {
        JSONObject props = super.doExecute(bound, context, wxContext);
        this.processRef(bound, "beginRef","begin", context, wxContext);
        this.processRef(bound, "endRef","end", context, wxContext);
        return props;
    }
}

编译前

<antdv:DatePicker.RangePicker bind:beginRef="restData0.current.ksrq" bind:endRef="restData0.current.jsrq" id="datePickerRangePicker0">
    </antdv:DatePicker.RangePicker>

编译后

<DatePicker.RangePicker id="datePickerRangePicker0" beginRefDataId="restData0" beginRefColumnName="ksrq" beginRefRow={((_exRun('restData0.current','restData0'))(restData0))} endRefDataId="restData0" endRefColumnName="jsrq" endRefRow={((_exRun('restData0.current','restData0'))(restData0))}>
    </DatePicker.RangePicker>

组件注册文件

组件注册文件的名称为:组件包名.components.xml。组件包下所有的组件,需要在此文件中注册后,才能使用。

<reg-info>
    <!-- 组件目录注册 -->
    <component-dirs framework="组件使用技术体系">
        组件名1,组件名2...
    </component-dirs>
    <uixide-toolbox framework="组件使用技术体系">
        <catalog name="分类名" order="分类排序">
            <item component-name="命名空间:组件名" device="桌面端/移动端"/>
        </catalog>
    </uixide-toolbox>
</reg-info>

其中

  • component-dirs:列举出要注册的组件
    • framework:表示该组件包中的组件所使用的技术体系,如:React 或 Vue
  • uixide-toolbox:组件面板注册信息
    • catalog:组件面板上的分类
      • name:分类名
      • order:分类序号
    • item:注册的组件
      • component-name:命名空间:组件名
      • device:pcx 表示桌面端,mx 表示移动端,不填表示两端皆可用

下面是 antdpro 组件包的注册文件中的部分代码

<reg-info>
    <!-- 组件目录注册 -->
    <component-dirs framework="react">
        Button,Icon
    </component-dirs>
    <uixide-toolbox framework="react">
        <catalog name="内容" order="3">
            <item component-name="antdpro:Button" device="pcx"  />
            <item component-name="antdpro:Icon" device="pcx"  />
        </catalog>
</reg-info>

配置目录

配置目录 config 包括设计器配置文件 studio.xml,用于标识组件注册文件所在路径。设计器配置文件代码如下

<?xml version="1.0" encoding="utf-8"?>
<config>
    <!-- 组件注册文件 -->
    <component-reg-file>../../components/*.components.xml</component-reg-file>
</config>

其他文件简介

组件包目录下还有几个文件,介绍如下

文件名 用途 说明
.comp.dist 标志文件 说明该组件是只读的组件,修改组件文件需要定制组件
.comp.save 标志文件 说明该组件是可读写的组件,定制组件后,删除 .comp.dist 文件,增加 .comp.save 文件
.framework.json 标志文件 标识组件使用的端以及技术体系
.restart 标志文件 说明组件从市场下载后,需要重启
命名空间.meta 描述文件 记录当前组件包是由哪个组件模版创建出来的。再次新建组件时,会根据文档信息拷贝相应模板资源,完成组件的创建
webpack.antdpro.dll.config.js 配置文件 webpack打包dll配置文件
pre-upload.sh 脚本文件 组件发布前,在这个生命周期执行自定义命令

服务端目录

组件包的服务端目录相当于一个服务模块,可定义数据集和服务。在组件包中添加数据集和服务,参考《数据模型》和《服务模型

特别说明一下两点

切换到组件开发端,添加组件包的页面、数据集和服务

在前端访问后端服务模型,使用页面对象的 request 和 getServiceUrl 方法,代码如下

    let $page = this.getPage();//React 获取页面对象
    let $page = usePage();//Vue   获取页面对象

    $page.request({
        url: $page.getServiceUrl("/sn/sn/nextbydate"),
        data: {
            key: key
        }
    }).then(function(data){
        if ((data.statusCode >= 200) && (data.statusCode < 300)){
            data = data.data;
            self.nextByDateCache[key] = data;
            resolve(data);
        }else{
            reject("获取序号出错");
        }
    });

results matching ""

    No results matching ""