母版-布局-DSL 渲染基础(v4)

概述

不论是母版布局还是DSL,我们统一使用XML进行定义,可以更好的提供结构化表述。

参考文档:

下面文档中未介绍到的Mask母版Layout布局,可以去数据库中base库的表base_layout_definitionbase_mask_definitiontemplate字段查看

母版

确定了主题、非主内容分发区域所使用组件和主内容分发区域联动方式的页面配置。

母版内容分为主内容分发区域与非主内容分发区域。非主内容分发区域一般包含顶部栏、底部栏和侧边栏。侧边栏可以放置菜单,菜单与主内容分发区域内容进行联动。

image.png

默认母板

<mask>
    <multi-tabs />
    <header>
        <widget widget="app-switcher" />
        <block>
            <widget widget="notification" />
            <widget widget="divider" />
            <widget widget="language" />
            <widget widget="divider" />
            <widget widget="user" />
        </block>
    </header>
    <container>
        <sidebar>
            <widget widget="nav-menu" height="100%" />
        </sidebar>
        <content>
            <breadcrumb />
            <block width="100%">
                <widget width="100%" widget="main-view" />
            </block>
        </content>
    </container>
</mask>

该模板中包含了如下几个组件:

  • mask:母版根标签
  • multi-tabs:多选项卡
  • header:顶部栏
  • container:容器
  • sldebar:侧边栏
    • nav-menu:导航菜单
  • content:主内容
    • breadcrumb:面包屑
    • block:div块
    • main-view:主视图;用于渲染布局和DSL等相关内容;

母版将整个页面的大体框架进行了描述,接下来将主要介绍布局和DSL是如何在main-view中进行渲染的。关于自定义母版组件的相关内容 点击查看

布局

布局是将页面拆分成一个一个的小单元,按照从上到下、从左到右进行顺序排列

布局主要用于控制页面中元素的展示的相对位置,原则上不建议将元数据相关内容在布局中进行使用,可最大化布局的利用率。

默认表格视图(TABLE)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <xslot name="actions" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>

该模板中包含了如下几个组件:

  • view:视图;用于定义当前视图类型,不同的视图类型会有不同的数据交互,以及渲染不同的组件。
  • pack:容器类型相关组件。
  • element:元素组件;包含各种各样的组件,根据组件实现有不同的作用。
  • xslot:DSL插槽;用于将DSL中定义的模板分别插入到对应的槽中;

特别的,任何XML标签上的slot属性都具备DSL插槽的全部能力。当学习完DSL相关内容后,我们将会对DSL插槽有比较清晰的理解。

PS:在下面的内容中,将使用该布局进行描述。

DSL

准备工作

为了方便描述DSL和元数据之间的关系,我们需要先定义一个简单模型,这个模型里面包含字段和动作。这些通常是服务端定义的。(对服务端不感兴趣的同学可以跳过代码部分)

DemoModel.java
@Model.model(DemoModel.MODEL_MODEL)
@Model(displayName = "演示模型", labelFields = {"name"})
public class DemoModel extends IdModel {

    private static final long serialVersionUID = -7211802945795866154L;

    public static final String MODEL_MODEL = "demo.DemoModel";

    @Field(displayName = "名称")
    private String name;

    @Field(displayName = "是否启用")
    private Boolean isEnabled;
}
DemoModelAction.java
@Model.model(DemoModel.MODEL_MODEL)
@UxRouteButton(
        action = @UxAction(name = "redirectCreatePage", displayName = "创建", contextType = ActionContextTypeEnum.CONTEXT_FREE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
@UxRouteButton(
        action = @UxAction(name = "redirectUpdatePage", displayName = "编辑", contextType = ActionContextTypeEnum.SINGLE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
public class DemoModelAction {

    @Action(displayName = "启用")
    public DemoModel enable(DemoModel data) {
        data.setIsEnabled(true);
        data.updateById();
        return data;
    }

    @Action(displayName = "禁用")
    public DemoModel disable(DemoModel data) {
        data.setIsEnabled(false);
        data.updateById();
        return data;
    }
}

上面的java代码定义了演示模型字段动作

  • 字段 field
    • id:ID 整数 Integer
    • name:名称 字符串 String
    • isEnabled:是否启用 布尔 Boolean
  • 动作 action
    • redirectCreatePage:创建;目标视图为"演示模型form" 跳转动作 ViewAction
    • redirectUpdatePage:编辑;目标视图为"演示模型form" 跳转动作 ViewAction
    • enable:启用 提交动作 ServerAction
    • disable:禁用 提交动作 ServerAction

根据上面的元数据内容,我们可以定义一个如下所示的DSL模板,来使用所有的元数据将其渲染到页面中:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

该模板中包含的标签含义:

  • view:视图;与布局模板中的view标签作用一致。
  • template:向DSL插槽中插入一个模板片段
  • field:字段元数据标签;data属性与元数据中字段API名称(field)对应。
  • action:动作元数据标签;name属性与元数据中动作API名称(name)对应。

DSL模板本质上是对布局模板的补充,它使用布局模板中已经定义好的DSL插槽,对其进行相应规则的合并替换,最终构成一个可被执行的视图模板再经过客户端渲染展示给用户。单独解释DSL模板的结构化含义是完全没有意义的。

接下来,我们会分别定义不同的DSL模板,并结合默认表格视图的布局模板进行详细说明。

模板编译

服务端模板编译基础

进入页面时,客户端将通过加载跳转动作(ViewActionLoad)接口获取对应的布局模板和DSL模板,得到经过服务端编译JSON数据,类似于如下数据结构:(DSL模板的JSON结构)

{
    "dslNodeType": "VIEW",
    "type": "TABLE",
    ......,
    "widgets": [
        {
            "dslNodeType": "TEMPLATE",
            "slot": "search",
            "widgets": [
                ......
            ]
        },
        {
            "dslNodeType": "TEMPLATE",
            "slot": "actions",
            "widgets": [
                ......
            ]
        }
    ]
}

服务端编译过程可以简单的理解为以下几个内容:(实际要复杂很多,在这里我们仅需要关注最终拿到的JSON结果即可)

  • XML转换为JSON结构
  • XML标签转换为dslNodeType属性,XML子标签转换为widgets属性,其他属性保持不变
  • field标签(字段)action标签(动作)对应的元数据补充完整。如上所示,我们仅定义了字段的data属性,返回的JSON中将包含字段动作的全部元数据。字段会补充字段类型、显示名称、是否存储等元数据属性,动作会补充动作类型、显示名称等元数据属性。

示例模板

表格视图-布局模板
<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <xslot name="actions" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>
表格视图-DSL模板
<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

以上所示的两个模板(布局模板DSL模板)会作为布局和DSL的模板合并相关内容介绍的演示模板。更多的布局模板点击查看

布局和DSL的模板合并

当我们从服务端获取了布局模板DSL模板对应的JSON后,客户端将对这两个JSON进行合并处理。

合并操作主要是将DSL模板中定义的根标签下定义的所有template标签,向布局模板中定义的DSL插槽进行属性合并和子节点替换。

标准合并

将上述两个模板进行合并后,将得到如下结果:(为了方便观察结构,将使用XML结构进行解释,实际运行时为JSON结构)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search">
                <!-- slot="search" -->
                <field data="id" invisible="true" />
                <field data="name" />
                <field data="isEnabled" />
            </element>
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <!-- slot="actions" -->
            <action name="redirectCreatePage" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <!-- slot="fields" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,DSL模板定义的template标签下的内容被合并到了布局模板的对应位置中。接下来就可以进行模板渲染操作了。

子标签替换和属性合并

一般的,我们将处理布局模板中任何标签上的slot属性,以及xslot标签。

遵循就近原则,模板合并将取路径最短的模板进行替换和插入。

当我们需要设置表格所有字段都允许排序时,我们可以使用sortable="true"属性,设置在widget="table"组件上。

使用slot="fields"插槽是无法向widget="table"组件添加额外属性的,此时我们需要使用slot="table"这个插槽来实现我们的需求。

一个错误的DSL模板可能被定义成如下内容:(下面的示例舍弃了部分插槽内容)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
        </element>
    </pack>
</view>

由此可见,我们优先使用了widget="table"上的slot="table"属性作为插槽,将原本定义在widget="table"下的所有节点全部换成了在DSL模板中定义的内容,并且成功的添加了sortable="true"这一属性。

但是,由于子标签被完全替换了,因此丢弃了widget="rowActions"组件,导致slot="rowActions"中定义的内容,找不到对应的插槽,这部分DSL模板内容将被丢弃。

反向合并

由于上面的合并操作丢弃了widget="rowActions"组件,但我们的本意并不是想要丢弃这个组件,而是仍然希望能以某种规则将这部分内容继续保留。为了解决这个问题,我们提供了反向合并操作,但前提是,DOM结构必须是同一层级

基于这一特性,我们将上述模板稍加修改:(将slot="rowActions"放在slot="table"下面)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,原本定义在布局模板上的widget="rowActions"被找回来了,并且正确填充了定义在DSL模板中的内容。

不足的是,由于widget="table"下面的所有内容都被替换了一次,我们无法确定原本的widget="rowActions"组件应该如何正确放置,因此使用了DSL模板中定义的位置。

这可能会带来一些弊端,比如:将某一元素必须被放在第一个节点处。针对这种情况,布局模板本来是可以做到限定作用的,但由于使用了更大范围的插槽,使得这一特性丢失。

但也带来了一些灵活性,比如:大多数情况下,某一元素需要放在第一个节点处,但在特殊情况下,该元素需要放在最后一个节点处。

我们希望插槽可以设计的尽可能的灵活,以满足各种不同场景的需求。

属性插槽

当我们希望使用属性合并,但不希望替换子标签时,我们可以使用属性插槽来解决这个问题。

当我们希望向widget="group"组件上添加title="这是一个示例标题"属性时,我们可以使用slot="tableGroup"插槽。

我们可以使用如下所示的方式来定义DSL模板:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,当DSL模板中的template标签下没有任何子标签时,我们仅会处理属性合并,但不会处理在原布局模板下定义的子标签。

根据这一特性,我们也可以定义这样的DSL模板来实现上面给widget="table"添加sortable="true"的需求:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true" />
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:(与之前的合并结果完全一致)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由于属性插槽本身没有子标签,因此我们建议将所有的属性插槽配置在view标签子标签的最前面进行统一定义。

模板渲染

模板渲染是将一个包含dslNodeType属性的对象,生成对应框架的VDom对象,通过该VDom对象执行一段创建ts组件的逻辑,并通过ts组件渲染该组件对应的VDom对象

例如:使用Vue框架时,该VDom对象通常为VNode

(以下内容均围绕Vue框架进行描述,其他框架基本一致)

为了将模板渲染尽可能的接近第三方框架的渲染逻辑,我们采用了Tag(Vue组件) - Class Component(ts) - Component(Vue组件)这样的三层结构进行模板渲染的实现。

  • Tag(Vue组件):使用resolveComponent方法,将一个包含dslNodeType属性的对象,通过既定规则进行解析,获取一个Vue组件作为入口。主要处理模板标签和Vue组件之间的对应关系。
  • Class Component(ts):使用ts面向对象的特性,可以对组件逻辑进行重载、继承等处理,增加组件的灵活性。
  • Component(Vue组件):主要实现数据渲染。

具体的渲染逻辑不在此处进行介绍,该篇文章主要是为了让读者对模板以及整体框架有一个基本的概念,使用这套模板渲染的最终目的是为了让组件复用率更高,开发更加灵活。

Oinone社区 作者:oinone原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/23.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (1)
oinone's avataroinone
Previous 2023年6月20日 pm4:07
Next 2023年11月2日 pm1:58

相关推荐

  • 【前端】生产环境部署及性能调优

    概述 前端工程使用vue-cli-service进行构建,生成dist静态资源目录,其中包括html、css、javascript以及其他项目中使用到的所有资源。 在生产环境中,我们通常使用Nginx开启访问服务,并定位其访问目录至dist目录下的index.html,以此来实现前端工程的访问。 不仅如此,为了使得前端发起请求时,可以正确访问到后端服务,也需要在nginx中配置相应的代理,使得访问过程在同域中进行,以达到Cookie共享的目的。 当然,服务部署的形式可以有多种,上述的部署方式也是较为常用的部署方式。 部署 使用production模式进行打包 使用dotenv-webpack插件启用process.env 配置chainWebpack调整资源加载顺序 使用thread-loader进行打包加速 性能调优 使用compression-webpack-plugin插件进行压缩打包 启用Nginx的gzip和gzip_static功能 使用OSS加速静态资源访问(可选) 使用production模式进行打包 在package.json中添加执行脚本 { "scripts": { "build": "vue-cli-service build –mode production" } } 执行打包命令 npm run build 使用dotenv-webpack插件启用process.env 参考资料 dotenv-webpack 在package.json中添加依赖或使用npm安装 { "devDependencies": { "dotenv-webpack": "1.7.0" } } npm install dotenv-webpack@1.7.0 –save-dev 在vue.config.js中添加配置 const Dotenv = require('dotenv-webpack'); module.exports = { publicPath: '/', productionSourceMap: false, lintOnSave: false, configureWebpack: { plugins: [ new Dotenv() ] } }; .env加载顺序 使用不同模式,加载的文件不同。文件按照从上到下依次加载。 development .env .env.development production .env .env.production 配置chainWebpack调整资源加载顺序 chainWebpack对资源加载顺序取决于name属性,而不是priority属性。如示例中的加载顺序为:chunk-a –> chunk-b –> chunk-c。 code>test属性决定其打包范围,但集合之间会自动去重。如chunk-a打包node_modules下所有内容,chunk-b打包node_modules/@kunlun下所有内容。因此在chunk-a中将不再包含node_modules/@kunlun中的内容。没有

    2024年4月19日
    1.5K00
  • Oinone移动端快速入门

    介绍 oinone的pc端的页面默认都可以在移动端直接访问。自定义mask、layout、视图组件、字段组件、动作组件方式都参考pc端实现。目前移动端的UI组件是基于vant@3.6.0版本开发,如有自定义部分的代码,推荐使用该组件库。 “注意”: 由于移动端和pc端在交互上的巨大差异,两端用的是不同的UI组件库是,按照此约定开发的自定义组件在两端也是无法相互兼容的,在pc端自定义的组件或者页面,不会在移动端自动适配,需要自行开发对应的移动端组件或者页面。 工程搭建 移动端很多交互跟pc端差异很大,所以移动端的我们采用的方案是独立用一套移动端的UI框架实现,而不是简单的做页面布局自适应,所以移动端需要跟pc端一样独立部署一套前端工程。 参考文档:【前端】移动端工程结构最佳实践(v4/v5) 如何区分多端 在界面设计器设计页面的时候,可以通过顶部的多端设备的图标切换在各端的页面效果。 pc端页面 切换为移动端后的页面 注意:大部分情况下,pc端和移动端可以共享一个设计的页面,然后通过上面的方法区分两端,如果移动端的页面交互差异很大,那更推荐独立新建一个页面专门给移动端使用。 模块 模块在定义的时候可以通过注解@Module.clientTypes决定当前模块在哪些端展示 package pro.shushi.pamirs.demo.api; import org.springframework.stereotype.Component; import pro.shushi.pamirs.business.api.BusinessModule; import pro.shushi.pamirs.core.common.CommonModule; import pro.shushi.pamirs.file.api.FileModule; import pro.shushi.pamirs.meta.annotation.Module; import pro.shushi.pamirs.meta.base.PamirsModule; import pro.shushi.pamirs.meta.common.constants.ModuleConstants; import pro.shushi.pamirs.meta.enmu.ClientTypeEnum; import pro.shushi.pamirs.user.api.UserModule; @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", // 客户端类型,默认是PC和MOBILE端都展示 clientTypes = {ClientTypeEnum.PC, ClientTypeEnum.MOBILE}, // 登录后默认访问 priority 值最小的模块 priority = 1, dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, BusinessModule.MODULE_MODULE, FileModule.MODULE_MODULE } ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) public class DemoModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core"; public static final String MODULE_NAME = "DemoCore"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.demo" }; } } 菜单 界面设计器设置方式 在菜单设置的时候可以选择“显示设备” 低代码设置方式 通过注解@UxMenu.clientTypes设置显示设备 package pro.shushi.pamirs.demo.core.init.menu; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.demo.api.model.DemoItemCategory; import pro.shushi.pamirs.meta.enmu.ClientTypeEnum; @UxMenus() public class DemoMenus implements ViewActionConstants { // 同时在pc端和移动端显示 @UxMenu(value = "商品中心", clientTypes = {ClientTypeEnum.PC, ClientTypeEnum.MOBILE}) class ItemPMenu{ @UxMenu("商品类目") class DemoItemAndCateMenu { // 只在pc端显示 @UxMenu(value = "商品管理", clientTypes = {ClientTypeEnum.PC}) @UxRoute(DemoItem.MODEL_MODEL) class DemoItemMenu { } // 只在移动端显示 @UxMenu(value = "类目管理", clientTypes = {ClientTypeEnum.MOBILE}) @UxRoute(DemoItemCategory.MODEL_MODEL) class DemoItemCategoryMenu { } } } } 组件 界面设计器选中组件后,可以在右边属性面板看到“显示设备”的配置,默认为空,为空则表示在pc端和移动端都显示

    2024年9月19日
    2.6K00
  • 主题设置-设置表格全局样式

    在启动工程的main.ts通过主题配置表格全局样式 registerThemeItem('demoTheme', { 'table-config': { // 是否带有边框 default(默认), full(完整边框), outer(外边框), inner(内边框), none(无边框) border: 'full', // 是否带有斑马纹 stripe: false, // 当鼠标点击行时,是否要高亮当前行 isCurrent: true } as Partial<TableThemeConfig> }); VueOioProvider( { browser: { title: 'Oinone – 构你想象!', favicon: 'https://pamirs.oss-cn-hangzhou.aliyuncs.com/pamirs/image/default_favicon.ico' }, theme: ['demoTheme'], // 其他代码 });

    2024年8月2日
    1.5K00
  • 左树右表默认选择第一行

    import { BaseElementWidget, Widget, SPI, ViewType, TableSearchTreeWidget } from '@kunlun/dependencies'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'tree', model: '改成当前视图的模型' }) ) export class CustomTableSearchTreeWidget extends TableSearchTreeWidget { protected hasExe = false; @Widget.Watch('rootNode.children.length') protected watchRootNode(len) { if (len && !this.hasExe) { this.hasExe = true; const firstChild = this.rootNode?.children?.[0]; if (firstChild) { this.onNodeSelected(firstChild); this.selectedKeys = [firstChild.key]; } } } }

    2024年11月26日
    1.5K00
  • 如何提高自定义组件的开发效率

    引言 本文将通过前端的开发者模式带领大家提高自定义组件的开发效率 支持2024年9月6日之后用npm i安装的4.7.x之后的所有版本 介绍前端开发者模式 开发者模式的特性 浏览器控制台可以看到更多利于开发的日志输出 页面顶部状态栏消息模块的轮询接口,将只在页面刷新后请求一次,这样会减少开发阶段不必要的请求,以及解决后端断点调试的时候被消息轮询干扰的问题 页面视图的数据将不在走缓存,而是每次实时从base_view表里查询template字段获取,方便需要实时看通过xml修改的页面效果 如何进入开发者模式 通过在浏览器地址栏后追加;mode=dev刷新页面进入开发者模式,页面加载后可以在浏览器开发者工具的应用标签下的本地存储空间(即localStorage)中看到本标识的存储,如果想退出开发者模式可以手动清除该值 开发者模式的应用场景 1.通过控制台跳转到视图动作(ViewAction)的调试页面 页面刷新后,可以在浏览器开发者工具的控制台看到如下图的日志输出,直接点击该链接可以跳转到当前页面的调试链接弹窗视图动作、抽屉视图动作等以弹出层为展示效果的页面,之前的版本是无法进入调试链接查看的,现在可以在弹窗等视图动作点击后在控制台看到该链接点击进入调试页面 2.查看页面母版(mask)、布局(layout)的匹配情况 母版(mask)匹配结果布局(layout)匹配结果 3.查看视图组件(View)、字段组件(Field)、动作组件(Action)的匹配情况 通过浏览器开发者工具的控制台,我们可以看到每个视图、字段、字段匹配到的组件的class类名,这样我们就知道当前用的是那个组件,在自定义的时候可以在源码查看该组件类的实现代码,然后再选择是否继承该父类。另外我们在给组件定义SPI条件的时候,可以参考匹配入参和匹配结果内的属性。 匹配入参是当前元数据SPI的最全量注册条件 匹配结果是当前匹配到的组件的注册条件,一般情况下我们只需要在匹配结果的条件加上当前模型编码(model)、视图名称(viewName)、字段名称(name)、动作名称(actionName)等条件就可以完成自定义组件的覆盖。 在浏览器开发者工具的控制台,通过模型编码、视图名称、字段名称、动作名称等关键字快速搜索定位到需要查找的组件 控制台搜索快捷键:win系统通过ctrl+f,mac系统通过cmd+f 总结 在确保自定义部分代码最终正确被import导入到启动工程main.ts内的情况下,可以通过开发者模式在浏览器控制台打印的日志来排查组件未正确覆盖的问题。

    2024年9月6日
    2.1K00

Leave a Reply

Please Login to Comment