自定义视图部分区域渲染设计器的配置

自定义视图与界面设计器配置对接

在日常开发中,我们经常会遇到自定义视图的需求。自定义视图不仅需要与平台机制结合,还要实现与界面设计器中配置的字段和动作的无缝对接。本文将介绍如何将自定义视图与界面设计器中配置的字段和动作的无缝对接,实现字段和动作的渲染。

用大白话来讲就是:当前页面一部分是自定义的,一部分是设计器生成的

代码地址

目录

  1. 自定义表单视图与字段、动作的结合
  2. 自定义表格视图与字段、动作的结合

自定义表单视图与字段、动作的结合

首先需要在界面设计器配置好对应界面,虽然配置的页面样式跟期望展示的 UI 的不一样,但是数据的分发、汇总以及动作的交互也是一致的,所以我们可以通过自定义的方式替换这个页面的 UI,但是数据以及动作,是完全可以通过平台的能力获取的。

注册表单对应的 SPI

// CustomFormWidget.ts

import CustomForm from './CustomForm.vue';

@SPI.ClassFactory(
  BaseElementWidget.Token({
    viewType: ViewType.Form,
    widget: 'CustomFormWidget'
  })
)
export class CustomFormWidget extends FormWidget {
  public initialize(props: BaseElementObjectViewWidgetProps): this {
    super.initialize(props);
    this.setComponent(CustomForm);
    return this;
  }
}
<!-- CustomForm.vue -->

<template>
  <div class="custom-form-container">
    <div class="custom-form-tip">自定义视图</div>
  </div>
</template>
<script lang="ts">
  import { DslRender, DslRenderDefinition, PropRecordHelper } from '@kunlun/dependencies';
  import { createVNode, defineComponent, onMounted, PropType, ref, VNode } from 'vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      formData: {
        type: Object as PropType<Record<string, any>>,
        default: () => ({})
      }
    }
  });
</script>

在上述的代码中,我们继承的是 FormWidget,那么这个页面会自动发起对应的请求,所有的数据都在 formData 中。

表单视图渲染动作

在最开始我们讲到过,当前页面是在界面设计器配置过,所有在CustomFormWidget里面是可以拿到当前页面配置的元数据信息,所以我们可以拿到界面设计器配置的字段跟动作

/// CustomFormWidget.ts

@Widget.Method()
protected renderActionVNodes() {
  // 从dsl中获取actionBar,actionBar里面包含了界面设计器配置的动作
  const actionBar = this.metadataRuntimeContext.viewDsl?.widgets.find((w) => w.slot === 'actionBar');

  if (actionBar) {
    // actionBar.widgets 就是界面设计器配置的动作,借助平台提供的DslRender.render方法,将对应的dsl转换成VNode
    return actionBar.widgets.map((w, index) =>
      DslRender.render({
        ...w,
        __index: index
      })
    );
  }

  return null;
}

因为 renderActionVNodes 方法返回的是 VNode,所以我们必须借助 vue 的 render 函数才能渲染。

<!-- ActionRender.vue -->

<script lang="ts">
  import { defineComponent } from 'vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      renderActionVNodes: {
        type: Function,
        required: true
      }
    },
    render() {
      return this.renderActionVNodes();
    }
  });
</script>

在ActionRender.vue中,通过 props 接收renderActionVNodes 方法,最终在 render 函数中执行,所以只需要在CustomForm.vue导入ActionRender.vue。

<template>
  <div class="custom-form-container">
    <div class="custom-form-tip">自定义视图</div>

    <p style="color: red">下面是通过平台机制生成的动作</p>

    <div style="display: flex; gap: 10px">
      <action-render :renderActionVNodes="renderActionVNodes"></action-render>
    </div>
  </div>
</template>
<script lang="ts">
  import { DslRender, DslRenderDefinition, PropRecordHelper } from '@kunlun/dependencies';
  import { createVNode, defineComponent, onMounted, PropType, ref, VNode } from 'vue';
  import ActionRender from './components/ActionRender.vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      renderActionVNodes: {
        type: Function,
        required: true
      }
    },
    components: {
      ActionRender
    }
  });
</script>
<style lang="scss">
  .custom-form-container {
    .custom-form-tip {
      color: var(--oio-primary-color);
      margin-bottom: var(--oio-margin);
      font-size: 24px;
    }
  }
</style>

表单视图渲染字段

渲染界面设计器配置的字段和刚刚讲到的动作是一致的。

// CustomFormWidget.ts
@Widget.Method()
  protected renderFieldVNodes() {
    // 获取界面设计器配置的字段
    const modelFields = this.metadataRuntimeContext.model.modelFields;

    if (modelFields.length) {
      //借助平台提供的DslRender.render方法,将对应的dsl转换成VNode
      return modelFields.map((w) => DslRender.render(w.template!));
    }

    return null;
  }

因为renderFieldVNodes 返回的也是 VNode,所以我们还是需要借助 vue 的 render 函数来渲染.

<!-- FieldRender.vue -->
<script lang="ts">
  import { defineComponent } from 'vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      renderFieldVNodes: {
        type: Function,
        required: true
      }
    },
    render() {
      return this.renderFieldVNodes();
    }
  });
</script>

最终也是在CustomForm.vue中导入FieldRender.vue。

下面是完整的代码:

// CustomFormWidget.ts

import {
  BaseElementObjectViewWidgetProps,
  BaseElementWidget,
  DslRender,
  FORM_WIDGET,
  FormWidget,
  SPI,
  ViewType,
  Widget
} from '@kunlun/dependencies';

import CustomForm from './CustomForm.vue';

@SPI.ClassFactory(
  BaseElementWidget.Token({
    viewType: ViewType.Form,
    widget: 'CustomFormWidget'
  })
)
export class CustomFormWidget extends FormWidget {
  public initialize(props: BaseElementObjectViewWidgetProps): this {
    super.initialize(props);
    this.setComponent(CustomForm);
    return this;
  }

  /**
   * 渲染字段VNode
   */
  @Widget.Method()
  protected renderFieldVNodes() {
    const modelFields = this.metadataRuntimeContext.model.modelFields;

    if (modelFields.length) {
      return modelFields.map((w) => DslRender.render(w.template!));
    }

    return null;
  }

  /**
   * 渲染动作VNode
   */
  @Widget.Method()
  protected renderActionVNodes() {
    const actionBar = this.metadataRuntimeContext.viewDsl?.widgets.find((w) => w.slot === 'actionBar');

    if (actionBar) {
      return actionBar.widgets.map((w, index) =>
        DslRender.render({
          ...w,
          __index: index
        })
      );
    }

    return null;
  }

  @Widget.Reactive()
  protected showFieldVNode = false;

  protected async mountedProcess() {
    await super.mountedProcess();

    this.showFieldVNode = true;
  }
}
<!-- CustomForm.vue -->
<template>
  <div class="custom-form-container">
    <div class="custom-form-tip">自定义视图</div>

    <p style="color: red">下面是通过平台机制生成的字段</p>

    <field-render v-if="showFieldVNode" :renderFieldVNodes="renderFieldVNodes"></field-render>

    <p style="color: red">下面是通过平台机制生成的动作</p>

    <div style="display: flex; gap: 10px"><action-render :renderActionVNodes="renderActionVNodes"></action-render></div>
  </div>
</template>
<script lang="ts">
  import { DslRender, DslRenderDefinition, PropRecordHelper } from '@kunlun/dependencies';
  import { createVNode, defineComponent, onMounted, PropType, ref, VNode } from 'vue';
  import FieldRender from './components/FieldRender.vue';
  import ActionRender from './components/ActionRender.vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      renderActionVNodes: {
        type: Function,
        required: true
      },
      renderFieldVNodes: {
        type: Function,
        required: true
      },
    showFieldVNode: {
      type: Boolean,
      default: false
    }
    },
    components: {
      FieldRender,
      ActionRender
    }
  });
</script>
<style lang="scss">
  .custom-form-container {
    .custom-form-tip {
      color: var(--oio-primary-color);
      margin-bottom: var(--oio-margin);
      font-size: 24px;
    }
  }
</style>

注册 layout

registerLayout(
  `<view type="FORM">
    <element widget="CustomFormWidget" slot="form">
        <xslot name="fields" slotSupport="pack,field" />
    </element>
  </view>`,
  {
    moduleName: 'moduleName',
    model: 'model',
    viewType: ViewType.Form,
    actionName: 'actionName'
  }
);

自定义表格视图与字段动作的结合

注册表格对应的 SPI

// CustomTableWidget.ts

import {
  BaseElementObjectViewWidgetProps,
  BaseElementWidget,
  DslRender,
  SPI,
  TableWidget,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import CustomTable from './CustomTable.vue';

@SPI.ClassFactory(
  BaseElementWidget.Token({
    viewType: ViewType.Table,
    widget: 'CustomTableWidget'
  })
)
export class CustomTableWidget extends TableWidget {
  public initialize(props: BaseElementObjectViewWidgetProps): this {
    super.initialize(props);
    this.setComponent(CustomTable);
    return this;
  }

  /**
   * 渲染行内动作VNode
   */
  @Widget.Method()
  protected renderRowActionVNodes() {
   // const table = this.metadataRuntimeContext.viewDsl?.widgets.find((w) => w.slot === 'table');

    // const rowAction = table?.widgets.find((w) => w.slot === 'rowActions');
    // this.refreshCallChaining;
    // if (rowAction) {
    //   return rowAction.widgets.map((w) => DslRender.render(w));
    // }

    // return null;

    const modelActions = this.metadataRuntimeContext.model.modelActions;

    if (modelActions.length) {
      return modelActions.map((w) => DslRender.render(w.template!));
    }

    return null;
  }

  /**
   * 渲染动作VNode
   */
  @Widget.Method()
  protected renderActionVNodes() {
    const actionBar = this.metadataRuntimeContext.viewDsl?.widgets.find((w) => w.slot === 'actions');

    if (actionBar) {
      return actionBar.widgets.map((w, index) =>
        DslRender.render({
          ...w,
          __index: index
        })
      );
    }

    return null;
  }
}

在上述代码中,我们继承的是TableWidget,所以页面也会自动发起查询,并且定义了renderRowActionVNodes和renderActionVNodes方法,用于渲染行内动作和动作。

表格视图渲染动作

<!-- CustomTable.vue -->
<template>
  <div class="custom-table-container">
    <div class="custom-table-tip">自定义视图</div>

    <div style="display: flex; gap: 10px">
      <p style="color: red; margin-top: 10px">这是表格的动作</p>
      <action-render :renderActionVNodes="renderActionVNodes"></action-render>
    </div>

    <p style="color: red; margin-top: 10px">这是表格的数据跟行内动作</p>
    <div>
      <div v-for="(row, index) in dataSource" :key="row.index">
        <div style="display: flex">
          <div><span>行数据</span> <span> ID:{{ row.id }}</span></div>
          <div>
            <span>行动作</span>
            <span>
              <row-action-render
                :renderRowActionVNodes="renderRowActionVNodes"
                :row="row"
                :rowIndex="index"
                :parentHandle="currentHandle"
              ></row-action-render>
            </span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
  import { defineComponent, PropType } from 'vue';
  import ActionRender from './components/ActionRender.vue';
  import RowActionRender from './components/RowActionRender.vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
        currentHandle: {
          type: String,
          required: true
        },
      renderActionVNodes: {
        type: Function,
        required: true
      },
      renderRowActionVNodes: {
        type: Function,
        required: true
      },
      dataSource: {
        type: Array as PropType<any[]>,
        default: () => []
      }
    },
    components: {
      RowActionRender,
      ActionRender
    }
  });
</script>
<style lang="scss">
  .custom-table-container {
    .custom-table-tip {
      color: var(--oio-primary-color);
      margin-bottom: var(--oio-margin);
      font-size: 24px;
    }
  }
</style>

ActionRender.vue跟表单中的ActionRender.vue一样,内部也是在 render 函数中调用renderActionVNodes。

RowActionRender.vue有点特殊,下面是对应的代码(固定写法)。

<script lang="ts">
  import { ActionBar, RowActionBarWidget, uniqueKeyGenerator } from '@kunlun/dependencies';
  import { debounce } from 'lodash-es';
  import { createVNode, defineComponent } from 'vue';

  export default defineComponent({
    inheritAttrs: false,
    props: {
      row: {
        type: Object,
        required: true
      },
      rowIndex: {
        type: Number,
        required: true
      },
      renderRowActionVNodes: {
        type: Function,
        required: true
      },
      parentHandle: {
          type: String,
          required: true
        }
    },
    render() {
      const vnode = this.renderRowActionVNodes();

      return createVNode(
        ActionBar,
        {
          widget: 'rowAction',
          parentHandle: this.parentHandle,
          inline: true,
          activeRecords: this.row,
          rowIndex: this.rowIndex,
          key: this.rowIndex,
          refreshWidgetRecord: debounce((widget?: RowActionBarWidget) => {
            if (widget) {
              widget.setCurrentActiveRecords(this.row);
            }
          })
        },
        {
          default: () => vnode
        }
      );
    }
  });
</script>

注册 layout

import { registerLayout, ViewType } from '@kunlun/dependencies';

registerLayout(
  `<view type="TABLE">
    <element widget="CustomTableWidget" slot="table" slotSupport="field">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" slotSupport="field" />
            <element widget="rowActions" slot="rowActions" slotSupport="action" />
        </element>
</view>`,
  {
    moduleName: 'resource',
    model: 'resource.ResourceProvince',
    viewType: ViewType.Table
  }
);

本文讲解了自定义视图与界面设计器中配置的字段和动作的无缝对接,以实现自定义视图的功能,本质上是将界面设计器配置的字段、动作,渲染成对应的 VNode,然后在自定义的页面渲染。

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

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

Like (1)
汤乾华's avatar汤乾华数式员工
Previous 2024年9月12日 am11:23
Next 2024年9月12日 pm10:16

相关推荐

  • 【前端】IOC容器(v4)

    什么是IOC容器? IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合,更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的使程序的整个体系结构变得非常灵活。在运行期,在外部容器动态的将依赖对象注入组件,当外部容器启动后,外部容器就会初始化。创建并管理对象实例,以及销毁,这种应用本身不负责依赖对象的创建和维护,依赖对象的创建和维护是由外部容器负责的称为控制反转。 IOC(控制反转)和DI(依赖注入) IOC(Inversion of Control, 控制反转):通过外部容器管理对象实例的一种思想。DI(Dependency Injection, 依赖注入):IOC的一种实现方式。 作者简述 IOC是Spring框架(一种以Java为语言开发的框架)的核心,并贯穿始终。其面向接口的开发能力,使得服务调用方和服务提供方可以做到完全解耦,只要遵循接口定义的规则进行调用,具体服务的实现可以是多样化的。 对于前端,我们使用inversify进行了IOC的实现。其强大的解耦能力可以使得平台进行大量的抽象,而无需关系具体的实现。 接下来,我们将介绍IOC在开发中的基本运用。 API 为了方便起见,我们将IOC相关功能与组件SPI的调用方式放在了一起。(更高版本的平台版本将自动获得该能力) export class SPI { /** * register singleton service */ public static Service; /** * autowired service property/parameter in service */ public static Autowired; /** * service construct after execute method */ public static PostConstruct; /** * autowired service in widget */ public static Instantiate; /** * autowired services in widget */ public static Instantiates; /** * service construct after execute method in widget */ public static InstantiatePostConstruct; } 创建第一个服务 service/ProductService.ts import { ServiceIdentifier } from '@kunlun/dependencies'; /** * 产品 */ export interface Product { id: string; name: string; } /** * 产品服务 */ export interface ProductService { /** * 获取产品列表 */ getProducts(): Promise<Product[]>; /** * 通过ID获取产品 * @param id 产品ID */ getProductById(id: string): Promise<Product | undefined>; } /** * 产品服务Token */ export const ProductServiceToken = ServiceIdentifier<ProductService>('ProductService'); service/impl/ProductServiceImpl.ts import { SPI } from '@kunlun/dependencies'; import { Product, ProductService, ProductServiceToken } from '../ProductService'; @SPI.Service(ProductServiceToken) export class ProductServiceImpl implements ProductService { public async getProducts(): Promise<Product[]> { // request api get products return []; } public async getProductById(id:…

    前端 2023年11月1日
    1.4K00
  • 如何在Oinone 根据主题实现自定义组件样式

    在页面交互中,样式的变化是前端核心工作之一。本文介绍如何在Oinone平台中根据主题变化自定义组件样式。 介绍 Oinone平台提供了六种不同的主题设置,浅色大主题、浅色中主题、浅色小主题、深色大主题、深色中主题、深色小主题,默认采用浅色中主题。本文旨在指导如何在线或通过代码修改这些主题,以满足个性化需求。 基础知识 Oinone平台的默认主题为浅色中主题,用户可以根据喜好选择以下六种主题中的任何一种: 浅色大主题 浅色中主题 浅色小主题 深色大主题 深色中主题 深色小主题 在线修改主题 用户可以通过进入系统配置应用,并切换到系统风格配置菜单来在线修改主题。选择喜欢的主题并保存即可轻松更换。 代码修改主题 步骤示例 新建theme.ts文件 在项目的src目录下新建一个theme.ts文件。 定义主题变量 在theme.ts文件中定义主题名称和CSS变量,示例中将主色系替换为黑色。 export const themeName = ‘OinoneTheme’; export const themeCssVars = { ‘primary-color’: ‘black’, ‘primary-color-hover’: ‘black’, ‘primary-color-rgb’: ‘0, 0, 0’, ‘primary-color-focus’: ‘black’, ‘primary-color-active’: ‘black’, ‘primary-color-outline’: ‘black’, }; 在main.ts注册 import { registerTheme, VueOioProvider } from ‘@kunlun/dependencies’; // 引入注册主题组件 import { themeName, themeCssVars } from ‘./theme’; // 引入theme.ts registerTheme(themeName, themeCssVars);// 注册 VueOioProvider( { …other config theme: [themeName] // 定义的themeName传入provider中 }, [] ); 4: 刷新页面看效果 注意事项 确保在定义CSS变量时遵循主题设计规范。 正确引入theme.ts文件以避免编译错误。 总结 本文详细介绍了在Oinone平台中修改主题的两种方法:在线修改和代码修改。这些步骤允许开发者和用户根据个人喜好或项目需求,自定义界面的主题风格。

    2024年2月26日
    1.9K00
  • 打开弹窗/抽屉的动作如何在弹窗关闭后扩展逻辑

    介绍 在业务中,我们可能会遇到在弹窗关闭后执行业务逻辑的场景,这个时候可以通过自定义弹窗动作来实现 注意: oinone已经内置了弹窗内的动作触发后刷新主视图、刷新当前视图、提交数据的能力,可以通过界面设计器在动作的属性面板配置,本文档为内置能力不满足需求的场景使用 场景案例 弹窗动作组件示例 import { ActionType, ActiveRecord, BaseActionWidget, DialogViewActionWidget, SPI, ViewActionTarget, DisposeEventHandler, IPopupInstance, PopupManager, RuntimeAction, } from '@kunlun/dependencies'; /** * 弹出层销毁回调 – 建议抽到工具类中 * @param popupKey 弹出层key * @param disposeEventHandler 销毁的回调 */ function popupDisposeCallback( popupKey: string, disposeEventHandler: DisposeEventHandler, ) { const innerDisposeFn = (manager: PopupManager, instance: IPopupInstance, action?: RuntimeAction) => { if (instance.key === popupKey) { disposeEventHandler?.(manager, instance, action); } PopupManager.INSTANCE.clearOnClose(innerDisposeFn); }; PopupManager.INSTANCE.onClose(innerDisposeFn); } @SPI.ClassFactory( BaseActionWidget.Token({ actionType: [ActionType.View], target: [ViewActionTarget.Dialog], model: 'resource.k2.Model0000000109', name: 'dialogActionName001' }) ) export class CustomDialogViewActionWidget extends DialogViewActionWidget { protected createPopupWidget(data: ActiveRecord[]) { super.createPopupWidget(data); popupDisposeCallback(this.dialog.getHandle(), async (manager: PopupManager, instance: IPopupInstance, action?: RuntimeAction) => { // action为触发关闭弹窗的动作,点击动作关闭弹出层该参数才有值,如果是点击遮罩背景层则无该参数 if (action?.name === 'actionName001') { // 以下为示例代码,指定name的动作关闭弹窗后刷新当前视图的数据查询 this.refreshCallChaining?.syncCall(); } }); } } 函数式调用打开弹窗的示例 以下为在自定义字段组件中手动触发打开弹窗 import { BaseFieldWidget, Dialog, DialogWidget, DisposeEventHandler, FormStringFieldSingleWidget, IPopupInstance, ModelDefaultActionName, ModelFieldType, PopupManager, RuntimeAction, RuntimeViewAction, SPI, ViewType, Widget } from '@kunlun/dependencies'; /** * 弹出层销毁回调 – 建议抽到工具类中 * @param popupKey 弹出层key * @param disposeEventHandler 销毁的回调 */ function popupDisposeCallback( popupKey: string, disposeEventHandler: DisposeEventHandler, ) { const innerDisposeFn = (manager: PopupManager, instance: IPopupInstance, action?: RuntimeAction) => { if (instance.key === popupKey) { disposeEventHandler?.(manager, instance, action); } PopupManager.INSTANCE.clearOnClose(innerDisposeFn); }; PopupManager.INSTANCE.onClose(innerDisposeFn);…

    2024年8月22日
    1.5K00
  • 自定义字段组件如何处理vue组件内的表单校验

    介绍 本示例以字符串字段为业务场景,将输入框用element-plus的组件实现了一遍,vue组件内在onMounted生命周期内将ElForm表单实例通过ts组件内提供到props的setFormInstance方法设置到了ts组件的属性formInstance上,这样就可以在ts组件校验方法validator()触发的时候直接调用表单组件实例formInstance的校验方法validate() 适用场景 当前字段存储了动态表单的配置json,vue组件内自行实现了一套表单渲染逻辑,需要在vue组件和ts组件内同时触发校验 参考文档 element-plus表单组件文档 如何编写自定义字段组件的校验逻辑 示例代码 ts组件 import { BaseFieldWidget, FormStringFieldSingleWidget, isValidatorSuccess, ModelFieldType, SPI, ValidatorInfo, ViewType, Widget } from '@kunlun/dependencies'; import { FormInstance } from 'element-plus'; import MyFormStringField from './MyFormStringField.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.String, widget: 'Input', model: 'resource.k2.Model0000000109', name: 'code', }) ) export class MyFormStringFieldWidget extends FormStringFieldSingleWidget { public initialize(props) { super.initialize(props); this.setComponent(MyFormStringField); return this; } /** * ElementPlus的表单vue组件实例 * @private */ private formInstance?: FormInstance; @Widget.Method() private setFormInstance(formInstance: FormInstance | undefined) { this.formInstance = formInstance; } /** * 字段校验方法 */ public async validator(): Promise<ValidatorInfo> { const validRes = await this.formInstance?.validate((valid, fields) => {}); console.log('validRes', validRes) if (!validRes) { return this.validatorError('校验失败'); } const res = await super.validator(); if (!isValidatorSuccess(res)) { return res; } if (this.value == null) { return this.validatorSuccess(); } return this.validateLength(this.value); } } vue组件 <template> <ElForm ref="formInstance" :model="model" :rules="rules"> <ElFormItem label="编码" prop="code"> <ElInput v-model="model.code" @input="onValueChange"></ElInput> </ElFormItem> </ElForm> </template> <script lang="ts"> import { defineComponent, reactive, ref, onMounted, watch } from 'vue'; import { ElForm, ElFormItem, ElInput, FormInstance } from 'element-plus'; export default defineComponent({ name: 'MyFormStringField', components: { ElForm, ElFormItem, ElInput }, props: ['value', 'setFormInstance', 'onChange'],…

    2024年9月6日
    2.4K00
  • oio-cascader 级联选择

    级联选择框。 何时使用 需要从一组相关联的数据集合进行选择,例如省市区,公司层级,事物分类等。 从一个较大的数据集合中进行选择时,用多级分类进行分隔,方便选择。 比起 Select 组件,可以在同一个浮层中完成选择,有较好的体验。 API <oio-cascader :options="options" v-model:value="value" /> 参数 说明 类型 默认值 Version allowClear 是否支持清除 boolean true autofocus 自动获取焦点 boolean false changeOnSelect (单选时生效)当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 boolean false disabled 禁用 boolean false displayRender 选择后展示的渲染函数,可使用 #displayRender="{labels, selectedOptions}" ({labels, selectedOptions}) => VNode labels => labels.join(' / ') dropdownClassName 自定义浮层类名 string – getTriggerContainer 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 Function(triggerNode) () => document.body loadData 用于动态加载选项,无法与 showSearch 一起使用 (selectedOptions) => void – maxTagCount 最多显示多少个 tag,响应式模式会对性能产生损耗 number | responsive – maxTagPlaceholder 隐藏 tag 时显示的内容 v-slot | function(omittedValues) – multiple 支持多选节点 boolean – options 可选项数据源 – placeholder 输入框占位文本 string ‘请选择’ searchValue 设置搜索的值,需要与 showSearch 配合使用 string – showSearch 在选择框中显示搜索框 boolean false tagRender 自定义 tag 内容,多选时生效 slot – value(v-model:value) 指定选中项 string[] | number[] – showSearch showSearch 为对象时,其中的字段: 参数 说明 类型 默认值 filterOption 接收 inputValue path 两个参数,当 path 符合筛选条件时,应返回 true,反之则返回 false。 function(inputValue, path): boolean 事件 事件名称 说明 回调参数 版本 change 选择完成后的回调 (value, selectedOptions) => void – search 监听搜索,返回输入的值 (value) => void – Option interface Option { value: string | number; label?: any; disabled?: boolean; children?: Option[]; // 标记是否为叶子节点,设置了 `loadData` 时有效 // 设为 `false` 时会强制标记为父节点,即使当前节点没有 children,也会显示展开图标 isLeaf?: boolean; }

    2023年12月18日
    1.6K00

Leave a Reply

Please Login to Comment