自定义组件之自动渲染(组件插槽的使用)(v4)

阅读之前

你应该:

自定义组件简介

前面我们简单介绍过一个简单的自定义组件该如何被定义,并应用于页面中。这篇文章将对自定义组件进行详细介绍。

自定义一个带有具名插槽的容器组件(一般用于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) {
    super.initialize(props);
    this.setComponent(ListRenderDemo);
    return this;
  }
}
ListItemDemoWidget.ts
import { ActiveRecord, ActiveRecordsOperator, BaseElementWidget, SPI, Widget } from '@kunlun/dependencies';
import ListItemDemo from './ListItemDemo.vue';
import { ListItem } from './typing';

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ListItemDemo' }))
export class ListItemDemoWidget extends BaseElementWidget {
  @Widget.Reactive()
  protected get formData(): ActiveRecord {
    return this.activeRecords?.[0] || {};
  }

  @Widget.Reactive()
  protected rowIndex: number | undefined;

  public initialize(props) {
    const slotContext = props.slotContext as ListItem;
    if (slotContext) {
      props.activeRecords = ActiveRecordsOperator.repairRecords(slotContext.data);
      this.rowIndex = slotContext.index;
    }
    super.initialize(props);
    this.setComponent(ListItemDemo);
    return this;
  }
}
ListRenderDemo.vue
<template>
  <div class="list-render-demo-wrapper">
    <div class="list-render-item-wrapper" v-for="item in list" :key="item.key">
      <slot v-bind="item" />
    </div>
  </div>
</template>

<script lang="ts">
import { ActiveRecord, uniqueKeyGenerator } from '@kunlun/dependencies';
import { computed, defineComponent, PropType } from 'vue';
import { ListItem } from './typing';

export default defineComponent({
  name: 'ListRenderDemo',
  props: {
    showDataSource: {
      type: Array as PropType<ActiveRecord[]>
    }
  },
  setup(props) {
    const list = computed<ListItem[]>(() => {
      return (
        props.showDataSource?.map((data, index) => {
          const item: ListItem = {
            key: data.__draftId || uniqueKeyGenerator(),
            data,
            index
          };
          return item;
        }) || []
      );
    });

    return {
      list
    };
  }
});
</script>
ListItemDemo.vue
<template>
  <div class="list-item-demo">
    <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: 'ListItemDemo'
});
</script>

在一个画廊(GALLERY)的DSL模板中,我们可以这样使用这三个插槽:

<view type="GALLERY">
    <template slot="gallery" widget="ListRenderDemo">
        <element widget="ListItemDemo">
            <template slot="default">
                <field data="id" />
            </template>
            <template slot="title">
                <field data="name" />
            </template>
            <template slot="footer">
                <field data="isEnabled" />
            </template>
        </element>
    </template>
</view>

PS:从默认提供的画廊视图(GALLERY)布局来看,我们可以通过slot="gallery"插槽完全替换下面的子标签,并指定widget="ListRenderDemo"来使用我们的自定义组件。

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

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

(0)
oinone的头像oinone
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • OioNotification 通知提醒框

    全局展示通知提醒信息。 何时使用 在系统四个角显示通知提醒信息。经常用于以下情况: 较为复杂的通知内容。 带有交互的通知,给出用户下一步的行动点。 系统主动推送。 API OioNotification.success(title,message, config) OioNotification.error(title,message, config) OioNotification.info(title,message, config) OioNotification.warning(title,message, config) config 参数如下: 参数 说明 类型 默认值 版本 duration 默认 3 秒后自动关闭 number 3 class 自定义 CSS class string –

    2023年12月18日
    53100
  • 表格主题配置(v4)

    TableThemeConfig /** * 表格主题配置 */ export interface TableThemeConfig { border: boolean | string; stripe: boolean; isCurrent: boolean; isHover: boolean; /** * 表格列主题配置 */ column: Partial<TableColumnThemeConfig>; } /** * 表格列主题配置 */ export interface TableColumnThemeConfig { /** * <h3>最小宽度</h3> * <ul> * <li>boolean: enabled column width auto compute</li> * <li>number: using css width (default: px)</li> * <li>string: using css width</li> * <li> * object: auto compute width for label by default function * <ul> * <li>min: min min width (default: 120)</li> * <li>max: max min width (default: 432)</li> * <li>chineseWidth: chinese width (default: 14 -> fontSize: 14px)</li> * <li>otherWidth: non chinese width (default: 9 -> fontSize: 14px)</li> * <li>sortableFixWidth: sortable handle width (default: 40)</li> * <li>nonSortableFixWidth: non sortable fix width (default: 22)</li> * </ul> * </li> * <li>function: auto compute width for label by function</li> * </ul> */ minWidth: boolean | number | string | Partial<TableColumnMinWidthComputeConfig> | TableColumnMinWidthComputeFunction; /** * 操作列 */ operation: { /** * 宽度 (default: 165) */ width?: number | string; /** * 最小宽度 (default: 120) */ minWidth?: number | string; }; } export interface TableColumnMinWidthComputeConfig { min: number;…

    2023年11月6日
    58400
  • 动作API

    ActionWidget 动作组件的基类,包含了动作组件的通用属性和方法 示例 class MyActionWidget extends ActionWidget { } 动作属性 属性名 说明 类型 可选值 默认值 label 动作的名称 String – 当前动作的displayName action 当前动作的元数据 RuntimeAction – model 运行时模型 RuntimeModel – viewAction 运行时视图动作 RuntimeViewAction – view 运行时视图 RuntimeViewAction – initialValue 视图初始值 ActiveRecord[] – initialContext 视图初始上下文 Object – urlParameters 获取url参数 UrlQueryParameters – scene 场景 String – loading 动作加载状态 Boolean – false disabled 是否禁用 Boolean – false disabledTitle 禁用时的按钮名称 String – – invisible 当前字段是否不可见 Boolean – false validateForm 点击动作后是否校验表单 Boolean – false actionDomain 动作的domain查询条件 String – undefined goBack 点击动作后是否返回上一页 Boolean – false isDialog 是否为弹窗内动作 Boolean – 弹窗下的动作默认为true closeDialog 点击动作后是否关闭弹窗 Boolean – 默认为isDialog的值 isDrawer 是否为抽屉内动作 Boolean – 抽屉下的动作默认为true closeDrawer 点击动作后是否关闭抽屉 Boolean – 默认为isDrawer的值 isInnerPopup 是否为页内弹出层动作 Boolean – 页内弹出层下的动作默认为true isAsync 是否为异步动作 Boolean – true refreshRoot 是否刷新根视图 Boolean – false refreshData 是否刷新数据 Boolean – true type 动作的类型 ButtonType – 行内动作默认为ButtonType.link,其他动作为ButtonType.primary bizStyle 动作的业务类型 ButtonBizStyle – ButtonBizStyle.default icon 动作的图标 String – – enableConfirm 是否开启二次确认 Boolean – true confirmType 二次确认的类型 ConfirmType – – confirm 二次确认的内容 String – – confirmText 二次确认的提示内容 String – – confirmPosition 二次确认提示的展示位置 PopconfirmPlacement – PopconfirmPlacement.BM enterText 二次确认的确定按钮文字 String – – cancelText 二次确认的取消按钮文字 String – – searchBody 列表页的动作可以拿到搜索区域的搜索条件 ActiveRecord…

    2024年3月8日
    86300
  • 前端密码加密

    在项目开发中,我们可能会遇到自定义登录页,密码需要加密,或者是数据提交的时候,某个数据需要加密,在平台的前端中,提供了默认的全局加密 API 在 oinone 前端工程使用 // pc端工程使用 import { encrypt } from '@kunlun/dependencies'; // 移动端端工程使用 import { encrypt } from '@kunlun/mobile-dependencies'; // 加密后的密码 const password = encrypt('123456'); 其他工程使用 如果是其他工程,前端没有用到 oinone 这一套,比如小程序,或者是其他工程,可以使用下面的代码记得安装 crypto-js import CryptoJS from 'crypto-js'; const key = CryptoJS.enc.Utf8.parse('1234567890abcdefghijklmnopqrstuv'); const iv = CryptoJS.enc.Utf8.parse('1234567890aabbcc'); export const encrypt = (content: string): string => { if (typeof content === 'string' && content) { const encryptedContent = CryptoJS.AES.encrypt(content, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encryptedContent.ciphertext.toString(); } return ''; };

    2025年3月24日
    25400
  • oio-empty-data 空数据状态

    何时使用 当目前没有数据时,用于显式的用户提示。 初始化场景时的引导创建流程。 API 参数 说明 类型 默认值 版本 description 自定义描述内容 string | v-slot – image 设置显示图片,为 string 时表示自定义图片地址 string | v-slot false imageStyle 图片样式 CSSProperties –

    2023年12月18日
    59600

Leave a Reply

登录后才能评论