自定义组件之手动渲染弹出层(v4)

阅读之前

你应该:

弹出层组件

我们内置了两个弹出层组件,弹窗(Dialog)抽屉(Drawer),以下所有内容全部围绕弹窗(Dialog)进行描述,抽屉相关内容与弹窗完全一致。

下面这个对照表格可以帮助你区分两个弹出层组件的异同:

弹出层相关功能 弹窗(Dialog) 抽屉(Drawer)
API方法 Dialog#create Drawer#create
内置组件 DialogWidget DrawerWidget
内置插槽 header, default, footer header, default, footer

渲染弹出层的方式

我们提供了三种渲染弹出层组件的方式,根据不同的情况使用不同的方式可以让实现变得更简单。

  • 使用Dialog#createAPI方法创建弹窗:一般用于简单场景,动作区无法进行自定义。
  • 使用DSL渲染能力创建弹窗
    • 调用ActionWidget#click方法打开弹窗(适用于自动渲染场景)
    • 使用createWidget创建DialogWidget并打开弹窗(适用于手动渲染场景)
  • 使用第三方组件创建弹窗:一般用于弹窗需要自定义的场景中,性能最优,可用于任何场景。

使用Dialog#createAPI方法创建弹窗

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

view.ts
export const template = `<view>
    <field data="id" invisible="true" />
    <field data="code" label="编码" />
    <field data="name" label="名称" />
</view>`;
ManualDemoWidget.ts
import {
  BaseElementWidget,
  createRuntimeContextForWidget,
  Dialog,
  FormWidget,
  MessageHub,
  RuntimeView,
  SPI,
  ViewCache,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import ManualDemo from './ManualDemo.vue';
import { template } from './view';

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' }))
export class ManualDemoWidget extends BaseElementWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(ManualDemo);
    return this;
  }

  @Widget.Method()
  public async openPopup() {
    // 获取运行时视图
    const view = await this.fetchViewByCompile();
    if (!view) {
      console.error('Invalid view');
      return;
    }

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

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

    // 创建弹窗
    const dialogWidget = Dialog.create();

    // 设置弹窗属性
    dialogWidget.setTitle('这是一个演示弹窗');

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

    dialogWidget.on('ok', () => {
      MessageHub.info('click ok');

      // 关闭弹窗
      dialogWidget.onVisibleChange(false);
    });

    dialogWidget.on('cancel', () => {
      MessageHub.info('click cancel');

      // 关闭弹窗
      dialogWidget.onVisibleChange(false);
    });

    // 打开弹窗
    dialogWidget.onVisibleChange(true);
  }

  public async fetchViewByCompile(): Promise<RuntimeView | undefined> {
    // 模型编码
    const model = ${model};

    // 通过编译获取视图(非完整视图数据)
    const view = await ViewCache.compile(model, '$$popup_demo', template);
    if (!view) {
      return;
    }
    // 补充视图类型
    view.type = ViewType.Form;

    return view;
  }
}
ManualDemo.vue
<template>
  <div class="manual-demo">
    <oio-button @click="openPopup">打开弹窗</oio-button>
  </div>
</template>
<script lang="ts">
import { OioButton } from '@kunlun/vue-ui-antd';
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'ManualDemo',
  components: {
    OioButton
  },
  props: {
    openPopup: {
      type: Function
    }
  }
});
</script>
视图DSL
<view type="FORM">
    <template slot="form">
        <field data="id" invisible="true" />
        <field data="code" />
        <field data="name" />
        <element widget="ManualDemo" />
    </template>
</view>

关键点详解

......

使用DSL渲染能力创建弹窗(调用ActionWidget#click方法)

以下是一个自定义组件的完整示例,视图被定义在DSL模板中。

ManualDemoWidget.ts
import { ActionWidget, BaseElementWidget, DslDefinitionWidget, SPI, Widget } from '@kunlun/dependencies';
import ManualDemo from './ManualDemo.vue';

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' }))
export class ManualDemoWidget extends BaseElementWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(ManualDemo);
    return this;
  }

  @Widget.Method()
  public async openPopup() {
    const actionWidget = this.getChildren().find((v) => {
      if (v instanceof DslDefinitionWidget) {
        const slotName = v.getSlotName();
        if (slotName === 'popupAction' && v instanceof ActionWidget) {
          return v;
        }
      }
      return false;
    }) as ActionWidget;

    if (actionWidget) {
      await actionWidget.click();
      actionWidget.forceUpdate();
    }
  }
}
ManualDemo.vue
<template>
  <div class="manual-demo">
    <oio-button @click="openPopup">打开弹窗</oio-button>
    <div style="display: none">
      <slot name="popupAction" />
    </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: {
    openPopup: {
      type: Function
    }
  }
});
</script>
视图DSL
<view type="FORM">
    <template slot="form">
        <field data="id" invisible="true" />
        <field data="code" />
        <field data="name" />
        <element widget="ManualDemo">
            <template slot="popupAction">
                <action name="$$popup_action_demo" actionType="VIEW" contextType="CONTEXT_FREE" target="DIALOG" resModel="${model}">
                    <template slot="default" title="这是一个演示弹窗">
                        <view type="FORM">
                            <template slot="actionBar">
                                <action name="$$internal_DialogCancel" type="default" />
                                <action name="create" />
                            </template>
                            <template slot="form">
                                <field data="id" invisible="true" />
                                <field data="code" />
                                <field data="name" />
                            </template>
                        </view>
                    </template>
                </action>
            </template>
        </element>
    </template>
</view>

关键点详解

......

使用DSL渲染能力创建弹窗(创建DialogWidget组件)

以下是一个自定义组件的完整示例,其使用ViewCache#compule方法获取视图,该视图对应的是弹窗组件的渲染模板。

view.ts
export const template = `<view title="这是一个演示弹窗">
    <template slot="default">
        <view type="FORM">
            <element widget="form">
                <field data="id" invisible="true" />
                <field data="code" />
                <field data="name" />
            </element>
        </view>
    </template>
    <template slot="footer">
        <element widget="actionBar">
            <action name="$$internal_DialogCancel" type="default" />
            <action name="create" />
        </element>
    </template>
</view>`;
ManualDemoWidget.ts
import {
  BaseElementWidget,
  createRuntimeContextForWidget,
  DialogWidget,
  RuntimeView,
  SPI,
  ViewCache,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import ManualDemo from './ManualDemo.vue';
import { template } from './view';

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ManualDemo' }))
export class ManualDemoWidget extends BaseElementWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(ManualDemo);
    return this;
  }

  @Widget.Method()
  public async openPopup() {
    // 获取运行时视图
    const view = await this.fetchViewByCompile();
    if (!view) {
      console.error('Invalid view');
      return;
    }

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

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

    const dialogWidget = this.createWidget(new DialogWidget(), 'popupWidget', {
      metadataHandle: runtimeContextHandle,
      rootHandle: runtimeContextHandle,
      dataSource: formData,
      activeRecords: formData,
      template: runtimeContext.viewTemplate,
      inline: true,
      mountedVisible: true
    });

    dialogWidget.on('ok', async () => {
      dialogWidget.onVisibleChange(false);
    });
    dialogWidget.on('cancel', () => {
      dialogWidget.onVisibleChange(false);
    });

    this.forceUpdate();
  }

  public async fetchViewByCompile(): Promise<RuntimeView | undefined> {
    // 模型编码
    const model = ${model};

    // 通过编译获取视图(非完整视图数据)
    const view = await ViewCache.compile(model, '$$popup_demo', template);
    if (!view) {
      return;
    }
    // 补充视图类型
    view.type = ViewType.Form;

    return view;
  }
}
ManualDemo.vue
<template>
  <div class="manual-demo">
    <oio-button @click="openPopup">打开弹窗</oio-button>
    <div style="display: none">
      <slot name="popupWidget" />
    </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: {
    openPopup: {
      type: Function
    }
  }
});
</script>
视图DSL
<view type="FORM">
    <template slot="form">
        <field data="id" invisible="true" />
        <field data="code" />
        <field data="name" />
        <element widget="ManualDemo" />
    </template>
</view>

关键点详解

......

使用第三方组件创建弹窗

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

view.ts
export const template = `<view>
    <field data="id" invisible="true" />
    <field data="code" label="编码" />
    <field data="name" label="名称" />
</view>`;
ManualDemoWidget.ts
import {
  BaseElementWidget,
  createRuntimeContextForWidget,
  FormWidget,
  MessageHub,
  RuntimeView,
  SPI,
  ViewCache,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import ManualDemo from './ManualDemo.vue';
import { template } 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 openPopup(): Promise<void> {
    // 获取运行时视图
    const view = await this.fetchViewByCompile();
    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.formWidget = this.createWidget(FormWidget, 'formWidget', {
      metadataHandle: runtimeContextHandle,
      rootHandle: runtimeContextHandle,
      dataSource: formData,
      activeRecords: formData,
      template: runtimeContext.viewTemplate,
      inline: true
    });
    this.forceUpdate();
  }

  @Widget.Method()
  public onEnter() {
    MessageHub.info('click ok');
    return true;
  }

  @Widget.Method()
  public onCancel() {
    MessageHub.info('click cancel');
    return true;
  }

  public async fetchViewByCompile(): Promise<RuntimeView | undefined> {
    // 模型编码
    const model = ${model};

    // 通过编译获取视图(非完整视图数据)
    const view = await ViewCache.compile(model, '$$popup_demo', template);
    if (!view) {
      return;
    }
    // 补充视图类型
    view.type = ViewType.Form;

    return view;
  }
}
ManualDemo.vue
<template>
  <div class="manual-demo">
    <oio-button @click="openModal">打开弹窗</oio-button>
    <oio-modal v-model:visible="visible" title="这是一个演示弹窗" :enter-callback="onEnter" :cancel-callback="onCancel">
      <slot name="formWidget" />
    </oio-modal>
  </div>
</template>
<script lang="ts">
import { ManualWidget } from '@kunlun/dependencies';
import { OioButton, OioModal } from '@kunlun/vue-ui-antd';
import { defineComponent, nextTick, ref } from 'vue';

export default defineComponent({
  name: 'ManualDemo',
  mixins: [ManualWidget],
  components: {
    OioButton,
    OioModal
  },
  props: {
    openPopup: {
      type: Function
    },
    onEnter: {
      type: Function
    },
    onCancel: {
      type: Function
    }
  },
  setup(props) {
    const visible = ref(false);

    const openModal = async () => {
      await props.openPopup?.();
      nextTick(() => {
        visible.value = true;
      });
    };

    return {
      visible,

      openModal
    };
  }
});
</script>
视图DSL
<view type="FORM">
    <template slot="form">
        <field data="id" invisible="true" />
        <field data="code" />
        <field data="name" />
        <element widget="ManualDemo" />
    </template>
</view>

关键点详解

......

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

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

(0)
nation的头像nation数式员工
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • 前端graphql拼接复杂的数据类型结构

    在前端开发中,有时需要自定义视图,但手写 GraphQL 查询语句非常繁琐,特别是当查询很复杂时。本文将介绍如何使用平台内置的API buildSingleItemParam 来简化这个过程。 使用方法 buildSingleItemParam 方法接受两个参数: 字段结构 数据 下面是一个示例代码: import { IModelField, buildSingleItemParam } from '@kunlun/dependencies'; const onSaveViewData = async (data) => { // 定义字段的数据结构 const modelFields = [ { name: 'conversationId', ttype: ModelFieldType.String }, { name: 'msgId', ttype: ModelFieldType.String }, { name: 'rating', ttype: ModelFieldType.Integer }, { name: 'tags', ttype: ModelFieldType.OneToMany, modelFields: [ { name: 'id', ttype: ModelFieldType.String }, { name: 'name', ttype: ModelFieldType.String } ] }, { name: 'text', ttype: ModelFieldType.String } ] as IModelField[]; // 构建 GraphQL 查询语句 const gqlStr = await buildSingleItemParam(modelFields, data); const gqlBody = `mutation { chatMessageFeedbackMutation { create( data: ${gqlStr} ) { conversationId msgId rating tags { id } text } } }`; // 向服务器发送请求 const rst = await http.query('ModuleName', gqlBody) // todo }; // 调用示例 onSaveViewData({ conversationId: '12', msgId: '123', tags: [ { id: '122', name: '222' }, { id: '122', name: '222' } ] }); 以上是使用 buildSingleItemParam 简化 GraphQL 查询语句的示例代码。通过这种方式,我们可以更高效地构建复杂的查询语句,提高开发效率。

    2023年11月1日
    2.0K00
  • 【界面设计器】自定义字段组件实战——表格字段内嵌表格

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 表格中的一对多(O2M)或多对多(M2M)字段使用表格展开。 演示内容:在【商品】的表格中存在【库存信息】这一列,这一列的内容通过表格展示【尺码】和【数量】两列。 业务分析及实现思路 从需求来看,我们需要实现一个【内嵌表格】组件,并且该组件允许在【表格】视图中使用。与之前不同的是,这里我们需要支持两个业务类型一对多(O2M)和多对多(M2M),即一个组件中包含两个元件。 在【内嵌表格】组件的属性面板中,我们需要再定义一个【内嵌表格配置】组件,用来选择内嵌表格中需要哪些字段进行组合,以及为每个组合提供一些基础配置。 这里需要理解一个基本概念,即【内嵌表格】的属性面板是【内嵌表格配置】的【执行页面】。所有组件的属性面板在【执行页面】时都是【表单】视图。 因此我们可以实现一个【内嵌表格配置】组件,并且该组件允许在【表单】视图中使用。其业务类型使用【文本】,我们在保存配置数据时,可以使用JSON数据结构来存储复杂结构。(这里的实现思路并非是最符合协议设定的,但可以满足绝大多数组件场景) 在【内嵌表格配置】组件中,我们可以允许用户添加/移除组合,并且每个组合有两个属性,【标题】和【字段】。 一些解释 看过【界面设计器】自定义字段组件实战——表格字段组合展示文章的读者可能很熟悉这一实现思路,会想当然的尝试将两个组件进行合并。这里我觉得有必要作出一些实现思路上的解释。 虽然在表面上看起来【组合列配置】和【内嵌表格配置】用到的属性完全一样,但在实现上,由于关联关系的查询需要在组件中特殊处理【透出字段(选项字段列表)】字段(【界面设计器】组件开发常见问题中对该属性进行了解释),才能查询到相应的关联数据。 不仅如此,这两个组件所代表的含义也完全不同。【组合列配置】是在一列中配置需要展示的字段,它在未来可能会增加【颜色(根据条件判断展示不同的颜色)】、【动作(可点击的行为)】等等诸多与之相关的属性。【内嵌表格配置】是在一列中配置表格中的多列,它在未来可能会增加【行高(控制表格行高)】、【支持排序(表格列支持排序)】等等诸多与之相关的属性。 在这里希望读者可以理解一点:相似并不代表相关。组件的抽象与归纳整理的不同点在于,抽象更需要关心其本身所代表的含义,而不是仅关注其相似程度。将多个相似度高但含义不同的组件进行归纳整理得到的只是一个含义不明,无法适应变化的组件。 因此,我们仍然使用两个不同的组件进行实现。 准备工作 此处你应该已经准备好了【商品】和【库存】两个模型,并且可以完整执行【商品】模型的全部【增删改查】操作。 业务模型定义(此处模型定义并非业务中正常使用的模型定义,仅作为演示使用) (以下仅展示本文章用到的模型字段,忽略其他无关字段。) 关联字段:-左侧表示当前模型中的字段API名称,右侧表示关联模型中的字段API名称。 商品(Item) 名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段 ID id 整数 否 – – – 编码 code 文本 否 128 – – 名称 name 文本 否 128 – – 库存信息 inventoryInfo 一对多 是 – 库存(Inventory) id – itemId 库存(Inventory) 名称 API名称 业务类型 是否多值 长度(单值长度) 关联模型 关联字段 ID id 整数 否 128 – – 商品 item 多对一 否 – 商品(Item) itemId – id 商品ID itemId 整数 否 – – – 尺码 size 文本 否 128 – – 库存 count 整数 否 – – – PS:如果是使用【模型设计器】创建这两个模型,在创建关联关系字段时必须使用双向关联,才能正确建立关联关系。 实现页面效果展示 表格视图 表单视图 库存信息配置 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建内嵌表格组件 创建内嵌表格元件(一对多) 创建内嵌表格元件(多对多) 创建内嵌表格配置组件 创建内嵌表格配置元件 设计内嵌表格元件属性面板 (两个元件的属性面板可以完全一致) 创建tableConfig字段,并切换至【内嵌表格配置】组件。 再拖入【透出字段(选项字段列表)】,并设置为隐藏。 设计内嵌表格配置元件属性面板 启动SDK工程进行组件基本功能开发 开发步骤参考 打开【表格】视图,将【库存信息】字段的组件切换为【内嵌表格】 在属性面板中看到【内嵌表格配置】组件,并优先实现【内嵌表格配置】组件。这里的属性面板就是【内嵌表格配置】组件对应的【执行页面】。 当【内嵌表格配置】组件可以按照预先设计的数据结构正确保存tableConfig属性时,可以在【内嵌表格】组件中的props定义中直接获取该属性,接下来就可以进行【内嵌表格】组件的开发。 代码实现参考 工程结构 typing.ts export interface InlineTableConfig { key: string; label?: string; field?: string; } FieldService.ts import { GenericFunctionService, IModelField, QueryPageResult } from '@kunlun/dependencies'; export class FieldService { private static readonly FIELD_MODEL = 'base.Field'; public static async fetchFieldsByModel(model: string, filter?: string): Promise<IModelField[]> { let rsql = `model == "${model}"`; if (filter) {…

    2023年11月1日
    68500
  • 前端自定义组件之单页面步骤条

    本文将讲解如何通过自定义,实现单页面的步骤条组件。其中每个步骤的元素里都是界面设计器拖出来的。 实现路径 整体的实现思路是界面设计器拖个选项卡组件,自定义这个选项卡,里面的每个选项页都当成一步渲染出来,每一步的名称是选项页的标题。 1. 界面设计器拖出页面 我们界面设计器拖个选项卡组件,然后在每个选项页里拖拽任意元素。完成后点击右上角九宫格,选中选项卡,填入组件 api 名称,作用是把选项卡切换成我们自定义的步骤条组件,这里的 api 名称和自定义组件的 widget 对应。最后发布页面,并绑定菜单。 2. 组件实现 widget 组件重写了选项卡,核心函数 renderStep,通过 DslRender.render 方法渲染界面设计器拖拽的元素,每一步的 step 又是解析选卡页得到的。 import { SPI, Widget, DefaultTabsWidget, BasePackWidget, DslDefinition, DslRender, DslDefinitionType, CallChaining, customMutation } from '@oinone/kunlun-dependencies'; import { VNode } from 'vue'; import NextStepSinglePage from './NextStepSinglePage.vue'; @SPI.ClassFactory(BasePackWidget.Token({ widget: 'NextStepSinglePage' })) export class NextStepSinglePageWidget extends DefaultTabsWidget { public initialize(props) { super.initialize(props); this.setComponent(NextStepSinglePage); return this; } @Widget.Reactive() public get invisible() { return false; } // 配置的每一步名称,解析选项页的标题 @Widget.Reactive() public get titles() { return this.template?.widgets?.map((item) => item.title) || []; } // region 上一步下一步配置 // 步骤数组,数组里的元素即步骤要渲染的内容 @Widget.Reactive() public get steps(): DslDefinition[] { // 每个 tab 是一个步骤,这里会有多个步骤 // 每个步骤里有多个元素,又是数组 // 所以这里是二维数组 const tabDsls: DslDefinition[][] = this.template?.widgets.map((item) => item.widgets) || []; // 对每个步骤的子元素们,外侧包一层 row 布局,所以变回了一维数组 return tabDsls.map((tabDsl) => { return { …(this.template || {}), dslNodeType: DslDefinitionType.PACK, widgets: tabDsl, widget: 'row', resolveOptions: { mode: 1 } }; }); } // 渲染步骤,每个步骤有多个子元素 @Widget.Method() public renderStep(step: DslDefinition): VNode | undefined { return DslRender.render(step); } // region 校验相关 // 校验的钩子 @Widget.Reactive() @Widget.Inject('validatorCallChaining') protected parentValidatorCallChaining: CallChaining<boolean> | undefined; // 校验步骤表单 @Widget.Method() public async onValidator(): Promise<boolean> { const res = await this.parentValidatorCallChaining?.syncCall(); return res…

    2025年7月8日
    36600
  • 自定义组件之自动渲染(组件插槽的使用)(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日
    75200
  • oio-switch 开关

    API 参数 说明 类型 默认值 版本 autofocus 组件自动获取焦点 boolean false checked(v-model: checked ) 指定当前是否选中 checkedValue | unCheckedValue false checkedChildren 选中时的内容 slot checkedValue 选中时的值 boolean | string | number true disabled 是否禁用 boolean false loading 加载中的开关 boolean false unCheckedChildren 非选中时的内容 slot unCheckedValue 非选中时的值 boolean | string | number false 事件 事件名称 说明 回调参数 change 变化时回调函数 Function(checked: boolean | string | number, event: Event)

    2023年12月18日
    61700

Leave a Reply

登录后才能评论