自定义组件之手动渲染基础(v4)

阅读之前

你应该:

为什么需要手动渲染

自定义组件之自动渲染(组件插槽的使用)(v4)文章中,我们介绍了带有具名插槽的组件可以使用DSL模板进行自动化渲染,并且可以用相对简单的方式与元数据进行结合。

虽然自动化渲染在实现基本业务逻辑的情况下,有着良好的表现,但自动化渲染方式也有着不可避免的局限性。

比如:当需要多个视图在同一个位置进行切换

在我们的平台中,界面设计器的设计页面,在任何一个组件在选中后,需要渲染对应的右侧属性面板。每个面板的视图信息是保存在对应的元件中的。根据元件的不同,找到对应的视图进行渲染。在单个视图中使用自动化渲染是无法处理这一问题的,我们需要一种可以局部渲染指定视图的方式,来解决这一问题。

获取一个视图

使用ViewCache获取视图
export class ViewCache {
  /**
   * 通过模型编码和名称获取视图
   * @param model 模型编码
   * @param name 名称
   * @param force 强制查询
   * @return 运行时视图
   */
  public static async get(model: string, name: string, force = false): Promise<RuntimeView | undefined>

  /**
   * 通过模型编码、自定义名称和模板获取编译后的视图(此视图非完整视图,仅用于自定义渲染使用)
   * @param model 模型编码
   * @param name 名称(用作缓存key)
   * @param template 视图模板
   * @param force 强制查询
   * @return 运行时视图
   */
  public static async compile(
    model: string,
    name: string,
    template: string,
    force = false
  ): Promise<RuntimeView | undefined>
}
  • ViewCache#get:用于服务端定义视图,客户端直接获取完整视图信息。
  • ViewCache#compile:用于客户端定义视图,通过服务端编译填充元数据相关信息,但不包含视图其他信息。

自定义一个带有具名插槽的组件,并提供切换视图的相关按钮

以下是一个自定义组件的完整示例,其使用ViewCache#compile方法获取视图。

view.ts
const template1 = `<view>
    <field data="id" invisible="true" />
    <field data="code" label="编码" />
    <field data="name" label="名称" />
</view>`;

const template2 = `<view>
    <field data="id" invisible="true" />
    <field data="name" label="名称" />
    <field data="code" label="编码" />
</view>`;

export const templates = {
  template1,
  template2
};
ManualDemoWidget.ts
import {
  BaseElementWidget,
  createRuntimeContextForWidget,
  FormWidget,
  RuntimeView,
  SPI,
  ViewCache,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import ManualDemo from './ManualDemo.vue';
import { templates } from './view';

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' }))
export class ManualDemoWidget extends BaseElementWidget {
  private formWidget: FormWidget | undefined;

  public initialize(props) {
    super.initialize(props);
    this.setComponent(ManualDemo);
    return this;
  }

  @Widget.Method()
  public async resetTemplate(key: string): Promise<void> {
    // 获取运行时视图
    const view = await this.fetchViewByCompile(key);
    if (!view) {
      console.error('Invalid view');
      return;
    }

    // 销毁旧组件
    this.formWidget?.dispose();
    this.formWidget = undefined;

    // 创建运行时上下文
    const runtimeContext = createRuntimeContextForWidget(view);
    const runtimeContextHandle = runtimeContext.handle;

    // 获取初始化数据
    const formData = await runtimeContext.getInitialValue();

    // 提交数据源到当前视图
    this.reloadDataSource(formData);
    this.reloadActiveRecords(formData);

    // 创建所需组件
    this.formWidget = this.createWidget(FormWidget, 'formWidget', {
      metadataHandle: runtimeContextHandle,
      rootHandle: runtimeContextHandle,
      template: runtimeContext.viewTemplate,
      inline: true
    });
  }

  public async fetchViewByCompile(key: string): Promise<RuntimeView | undefined> {
    const template = templates[key];
    if (!template) {
      console.error('error template', key);
      return;
    }

    // 模型编码
    const model = ${model};

    // 通过编译获取视图(非完整视图数据)
    const view = await ViewCache.compile(model, key, template);
    if (!view) {
      return;
    }

    // 补充视图类型
    view.type = ViewType.Form;
    return view;
  }

  protected mounted() {
    // 挂载时初始化指定视图
    this.resetTemplate('template1');
  }
}
ManualDemo.vue
<template>
  <div class="manual-demo-wrapper" v-show="!invisible">
    <oio-button @click="() => switchTemplate('template1')">切换模版1</oio-button>
    <oio-button @click="() => switchTemplate('template2')">切换模版2</oio-button>
    <div class="manual-demo-widget">
      <slot name="formWidget" />
    </div>
  </div>
</template>
<script lang="ts">
import { OioButton } from '@kunlun/vue-ui-antd';
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'ManualDemo',
  components: {
    OioButton
  },
  props: {
    invisible: {
      type: Boolean,
      default: undefined
    },
    resetTemplate: {
      type: Function
    }
  },
  setup(props) {
    const switchTemplate = (key: string) => {
      props.resetTemplate?.(key);
    };

    return {
      switchTemplate
    };
  }
});
</script>
视图DSL
<view type="FORM">
    <template slot="actionBar">
        <action name="create" label="创建" />
    </template>
    <template slot="form" widget="ManualDemo" />
</view>

在上述示例中,我们在view.ts定义了两个用于切换的视图模板,在视图DSL指定widget="ManualDemo"使用我们定义的组件,以此来实现我们的需求。

关键点详解

Class ComponentVue组件之间的交互

@Widget.Method()
public async resetTemplate(key: string): Promise<void> {
  ......
}
export default defineComponent({
  props: {
    resetTemplate: {
      type: Function
    }
  },
  setup(props) {
    const switchTemplate = (key: string) => {
      props.resetTemplate?.(key);
    };

    return {
      switchTemplate
    };
  }
});

使用@Widget.Method()装饰器,将resetTemplate方法通过props传递给Vue组件,通过Vue组件的用户行为(点击按钮)调用所需的执行逻辑。

在这个示例中,我们通过在setup中定义switchTemplate方法,来避免resetTemplate方法可能为造成的一些异常。在最佳实践中,我们通常不建议开发者直接使用props中定义的Function类型。在大多数情况下,在Class Component中定义的方法应该是尽可能抽象的,而不是为了某个Vue组件提供的特殊方法。

手动渲染常用步骤

  1. 获取运行时视图
  2. 销毁旧组件
  3. 创建运行时上下文
  4. 获取初始化数据
    • 模板中定义的初始化数据
    • 调用服务端接口获取
  5. 根据当前场景选择数据源持有者
    • 提交数据源到当前视图
    • 组件持有数据源
  6. 创建所需组件

一般情况下,我们仅需按照上述步骤就可以实现手动渲染功能。

特别的,我们为了保证组件的生命周期完整,并且在运行时不会出现异常挂载/卸载的情况,自动渲染手动渲染是不允许交叉使用的,并且根据组件所属的场景不同,手动渲染自动渲染更关注数据交互相关的内容。

有时可能会遇到数据交互相关的意外

  • 数据源无法共享导致的动作功能异常
  • 数据源未正确提交到所需组件,可能被某个具有数据源的组件拦截或穿透提交到更上层组件。

获取运行时视图

public async fetchViewByCompile(key: string): Promise<RuntimeView | undefined> {
  // 获取视图模板
  const template = templates[key];
  if (!template) {
    console.error('error template', key);
    return;
  }

  // 模型编码
  const model = ${model};

  // 通过编译获取视图(非完整视图数据)
  const view = await ViewCache.compile(model, key, template);
  if (!view) {
    return;
  }

  // 补充视图类型
  view.type = ViewType.Form;
  return view;
}

在这个示例中,我们通过客户端定义视图模板,传递给服务端进行编译得到运行时视图,并手动补充了视图类型。

但通常情况下,我们采用的是通过服务端定义视图模板,并根据上下文中提供的某些信息,动态获取这个视图的模型编码视图名称,并使用ViewCache#get方法获取的。

如下述方法定义:

public fetchView(model: string, name: string): Promise<RuntimeView | undefined> {
  return ViewCache.get(model, name);
}

创建运行时上下文

const runtimeContext = createRuntimeContextForWidget(view);
const runtimeContextHandle = runtimeContext.handle;
createRuntimeContextForWidget定义
/**
 * 为组件创建运行时上下文,一般用于直接创建组件的场景
 * 注:deep属性必须保持一致
 * @param view 运行时视图
 * @param options 创建可选项 默认值: mergeLayout = false, deep = true
 * @return 组件运行时上下文
 */
export function createRuntimeContextForWidget(
  view: RuntimeView,
  options?: { mergeLayout?: boolean; deep?: boolean }
): RuntimeContext

此处的运行时上下文是针对某个具体组件的模板进行创建的,它与其他通过自动化渲染方式获取的上下文有所不同。

一般的,我们将传入的运行时视图称为组件视图,由此生成的运行时上下文称为组件运行时上下文,将这个运行时上下文中获取的viewTemplate称为组件模板

template1为例:

<view>
    <field data="id" invisible="true" />
    <field data="code" label="编码" />
    <field data="name" label="名称" />
</view>

该模板的view标签并非真实渲染了一个视图组件,而是将其作为组件模板根标签,其标签名称在运行时是没有任何含义的。

对于上述创建的FormWidget组件来说,通过this.getDsl()方法获取的DslDefinition对象就是view标签

例如,我们将template1改为:

<view layout="horizontal">
    <field data="id" invisible="true" />
    <field data="code" label="编码" />
    <field data="name" label="名称" />
</view>

FormWidget组件中,通过this.getDsl().layout将获取到horizontal值。

创建所需组件

// 获取初始化数据
const formData = await runtimeContext.getInitialValue();

// 提交数据源到当前视图
this.reloadDataSource(formData);
this.reloadActiveRecords(formData);

// 创建所需组件
this.formWidget = this.createWidget(FormWidget, 'formWidget', {
  metadataHandle: runtimeContextHandle,
  rootHandle: runtimeContextHandle,
  template: runtimeContext.viewTemplate,
  inline: true
});

在示例中,我们将数据源提交到当前视图用于和其他组件共享数据源,并创建了一个平台提供的FormWidget组件。'formWidget'Vue组件中定义的插槽名称。

创建任何组件时,metadataHandlerootHandle template以及inline属性是必须的。

  • metadataHandlerootHandle:用于应用运行时上下文到当前组件。一般情况下,手动渲染组件的这两个值始终是保持一致的。
  • template:用于指定组件使用的组件模板
  • inline:用于决定当前组件是否与浏览器的URL进行交互。

获取组件模板的初始化数据

  • RuntimeContext#getDefaultValue:收集组件模板field标签定义的defaultValue属性,并返回一个对象
  • RuntimeContext#getInitialValue:在RuntimeContext#getDefaultValue方法的基础上,将field标签定义的relatedcompute属性进行了首次计算,并返回一个对象。(目前最新版本与RuntimeContext#getDefaultValue一致,但我们建议开发者使用该方法)

通常情况下,我们也需要调用服务端服务,来获取最终的初始化数据。在这里不做相关介绍。

根据当前场景选择数据源持有者

数据交互相关章节中,我们对数据源持有者有详细介绍,在这里不再赘述。

视图DSL定义中,我们使用ManualDemo组件的方式决定,我们在这里需要将数据源提交到当前视图,用于actionBar组件中定义的动作进行数据源共享。动作执行时的所需的数据源,就是我们这里提交给当前视图的数据源。

一般情况下,手动渲染组件的数据源不再通过组件的自动查询功能进行数据初始化,这又可能会导致数据交互流程变得难以理解。因此我们建议手动渲染组件的数据源总是在创建组件前就提前准备好的。

下面我们介绍如何让组件持有数据源,该方法并不适用于当前场景,但开发者有必要了解相关内容。

组件持有数据源
// 获取初始化数据
const formData = await runtimeContext.getInitialValue();

// 创建所需组件
this.formWidget = this.createWidget(FormWidget, 'formWidget', {
  metadataHandle: runtimeContextHandle,
  rootHandle: runtimeContextHandle,
  dataSource: formData,
  activeRecords: formData,
  template: runtimeContext.viewTemplate,
  inline: true
});

与之前不同的是,我们将dataSourceactiveRecords通过属性直接传递给组件实例即可。

在当前场景中,这样修改后的结果将导致页面中的按钮无法获取数据源,无法点击。

支持手动渲染的组件

在平台提供的所有组件中,并非所有组件都支持手动渲染。包括但不限于如下几个组件:

  • TableWidget:表格组件
  • FormWidget:表单组件
  • DetailWidget:表单组件(详情)

如何让组件支持手动渲染

支持手动渲染的组件与普通组件唯一的不同就是对metadataHandlerootHandle的处理。

Vue框架下,我们提供了mixin混入的方式,让开发者可以更简单的定义一个支持手动渲染的组件。

组件注册、组件定义都无需改变,仅需在对应的Vue组件中添加ManualWidget混入即可。如下所示:

<script lang="ts">
import { ManualWidget } from '@kunlun/dependencies';
import { defineComponent } from 'vue';

export default defineComponent({
  ......
  mixins: [ManualWidget],
  ......
});
</script>

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

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

(0)
数式-海波的头像数式-海波数式管理员
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 【界面设计器】自定义字段组件实战——轮播图

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户需要从【创建/编辑】页面中上传多张图片,并且在【详情】页面将这多张图片进行【轮播】展示。 业务分析 从需求来看,我们需要实现一个【轮播图】组件,并且该组件允许在【详情】视图中使用。在其他视图中,我们可以直接使用平台内置的【图片】组件,实现基础的编辑和展示功能。 名词解释 业务模型:需要进行可视化管理的存储模型或代理模型。 准备工作 你需要在某个业务模型下创建一个【表格视图】用于查看全部数据,创建【表单视图】用于创建/编辑数据,并创建【详情视图】展示必要的信息。(为了方便起见,你可以在所有视图中仅使用编码和名称两个字段) 你需要将【表格视图】绑定到某个菜单上,并通过【跳转动作】将三个视图进行关联,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) DemoModel 名称 API名称 业务类型 是否多值 长度(单值长度) 编码 code 文本 否 128 名称 name 文本 否 128 轮播图 carouselImages 文本 是 512 实现页面效果展示 表格视图 表单视图-创建 表单视图-编辑 详情视图 根据业务背景添加轮播图字段到所有视图 轮播图字段信息: 字段业务类型:文本 多值:是 使用组件:图片 无代码模型 在模型设计器创建轮播图字段,并从【组件库】-【模型】拖放至视图中即可。 PS:这里需要注意的是,在模型设计器中需要切换至专家模式,并确认字段长度为512,否则当URL超长时将无法保存。 低代码模型 与服务端同学确认字段,并从【组件库】-【模型】中拖放至视图中即可。 将上图中的【演示】数据进行【编辑】,并上传三张图片,在【详情视图】查看默认展示效果。 演示图片下载 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建轮播图组件 创建轮播图元件 根据业务背景,我们需要根据模型中的字段确定业务类型,在这个场景中,可以使用如下配置。(暂时可以不进行属性面板的设计) 在【详情视图】中将【轮播图字段】的组件切换为我们新创建的【轮播图组件】 PS:这里会发现组件变成了【输入框】的样式,这是由于我们没有提供对应元件的代码实现,使得SPI找到了默认组件。 启动SDK工程进行组件基本功能开发 (npm相关操作请自行查看SDK工程中内置的README.MD) Carousel.vue <template> <a-carousel class="carousel" effect="fade" autoplay> <div class="carousel-item" v-for="image in images" :key="image"> <img :src="image" :alt="image" /> </div> </a-carousel> </template> <script lang="ts"> import { Carousel as ACarousel } from 'ant-design-vue'; import { computed, defineComponent, PropType } from 'vue'; export default defineComponent({ name: 'Carousel', components: { ACarousel }, props: { value: { type: Array as PropType<string[]> } }, setup(props) { const images = computed(() => props.value || []); return { images }; } }); </script> <style lang="scss"> .carousel { .slick-slide { height: 160px; & > div, .carousel-item { width: 100%; height: 100%; } img { max-width: 100%; max-height: 100%; margin: auto; } } } </style> 效果展示 开发完成后,我们将重新打包生成的JS文件和CSS文件在【界面设计器】的【低无一体】进行上传,就可以在【设计器环境】中正常使用了。 设计轮播图的属性面板 通过我们使用的a-carousel组件,我们发现组件中提供了很多【属性】或【功能】可以进行配置,比如是否自动切换(autoplay)、面板指示点位置(dotPosition)、是否显示面板指示点(dots)等。在这里我们将对这三个属性的配置方式进行演示,其他更多属性可以自行设计并开发。 我们可以在【界面设计器】的【属性面板设计】中根据这三个属性的字段类型确定以下信息: 功能 API名称 业务类型 选用组件 可选项 是否自动切换 autoplay 布尔 开关 -…

    2023年11月1日
    3.4K00
  • 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日
    1.2K00
  • 自定义组件之自动渲染(组件插槽的使用)(v4)

    阅读之前 你应该: 了解DSL相关内容。母版-布局-DSL 渲染基础(v4) 了解SPI机制相关内容。组件SPI机制(v4.3.0) 自定义组件简介 前面我们简单介绍过一个简单的自定义组件该如何被定义,并应用于页面中。这篇文章将对自定义组件进行详细介绍。 自定义一个带有具名插槽的容器组件(一般用于Object数据类型的视图中) 使用BasePackWidget组件进行注册,最终体现在DSL模板中为<pack widget="SlotDemo">。 SlotDemoWidget.ts import { BasePackWidget, SPI } from '@kunlun/dependencies'; import SlotDemo from './SlotDemo.vue'; @SPI.ClassFactory(BasePackWidget.Token({ widget: 'SlotDemo' })) export class SlotDemoWidget extends BasePackWidget { public initialize(props) { super.initialize(props); this.setComponent(SlotDemo); return this; } } 定义一个Vue组件,包含三个插槽,分别是default不具名插槽、title具名插槽、footer具名插槽。 SlotDemo.vue <template> <div class="slot-demo-wrapper" v-show="!invisible"> <div class="title"> <slot name="title" /> </div> <div class="content"> <slot /> </div> <div class="footer"> <slot name="footer" /> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'SlotDemo', props: { invisible: { type: Boolean, default: undefined } } }); </script> 在一个表单(FORM)的DSL模板中,我们可以这样使用这三个插槽: <view type="FORM"> <pack widget="SlotDemo"> <template slot="default"> <field data="id" /> </template> <template slot="title"> <field data="name" /> </template> <template slot="footer"> <field data="isEnabled" /> </template> </pack> </view> 这样定义的一个组件插槽和DSL模板就进行了渲染上的结合。 针对不具名插槽的特性,我们可以缺省slot="default"标签,缺少template标签包裹的所有元素都将被收集到default不具名插槽中进行渲染,则上述DSL模板可以改为: <view type="FORM"> <pack widget="SlotDemo"> <field data="id" /> <template slot="title"> <field data="name" /> </template> <template slot="footer"> <field data="isEnabled" /> </template> </pack> </view> 自定义一个数组渲染组件(一般用于List数据类型的视图中) 由于表格无法体现DSL模板渲染的相关能力,因此我们以画廊视图(GALLERY)进行演示。 先定义一个数组每一项的数据结构: typing.ts export interface ListItem { key: string; data: Record<string, unknown>; index: number; } ListRenderDemoWidget.ts import { BaseElementListViewWidget, BaseElementWidget, SPI } from '@kunlun/dependencies'; import ListRenderDemo from './ListRenderDemo.vue'; @SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ListRenderDemo' })) export class ListRenderDemoWidget extends BaseElementListViewWidget { public initialize(props)…

    2023年11月1日
    79300
  • 多对多的表格 点击添加按钮打开一个表单弹窗

    多对多的表格 点击添加按钮打开一个表单弹窗 默认情况下,多对多的表格上方的添加按钮点击后,打开的是个表格 ,如果您期望点击添加按钮打开的是个表单页面,那么可以按照下方的操作来 1: 先从界面设计器拖一个多对多的字段进来 2: 将该字段切换成表格,并拖入一些字段到表格上 3: 选中添加按钮,将其隐藏 4: 从组件区域的动作分组中拖一个跳转动作,并且进行如下的配置 5: 属性填写好后进行保存,然后在设计弹窗 6: 拖入对应的字段到弹窗中, 当弹窗界面设计完成后,再把保存的按钮拖入进来 这样多对多的添加弹窗就变成了表单

    2023年11月9日
    92200
  • 移动端5.0.x启动、打包代码报错

    在5.0.x版本中,移动端mobile-base包是源码开放的,所以项目在启动的时候可能会报错,请按照下面的步骤修改。 打开boot工程中的package.json "dependencies"中添加 "lodash-es": "4.17.21" "devDependencies"中添加 "@types/lodash-es": "4.17.12" 在main.ts中删除 import '@kunlun/vue-mobile-base/dist/kunlun-vue-mobile-base.css'

    2024年7月17日
    1.2K00

Leave a Reply

登录后才能评论