自定义的「视图、字段」调用界面设计器配置的按钮(包含权限控制)

我们在业务开发中,经常会遇到自定义的视图或字段。有时候期望点击某一块区域的时候,打开一个弹窗或者是跳转新页面亦或者是执行服务端动作(调接口),但是希望这个动作是界面设计器拖拽进来的。

这篇文章详细的讲解了自定义的视图、字段怎么执行界面设计器拖出来的按钮。

自定义视图

1: 先设计一个页面,把对应的动作拖拽进来,可以不需要配置字段
数式Oinone低代码-自定义的「视图、字段」调用界面设计器配置的按钮
2: 将该页面绑定菜单

3: 自定义对应的页面

当我们自定义视图的时候,首先会注册一个视图,下面是我自定义的一个表单视图

registerLayout(
  `<view type="FORM">
    <element widget="actionBar" slot="actionBar">
        <xslot name="actions" />
    </element>
    <element widget="MyWidget" slot="form">
        <xslot name="fields" />
    </element>
</view>`,
  {
    moduleName: 'ys0328',
    model: 'ys0328.k2.Model0000000453',
    actionName: 'MenuuiMenu78ec23b054314ff5a12b4fe95fe4d7b5',
    viewType: ViewType.Form
  }
);

我自定义了一个叫做MyWidget的 element,下面是对应的ts代码

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

这是对应的 vue 文件: MyVue.vue

<template>
  <div @click="onClick">点击执行动作</div>
</template>

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

  export default defineComponent({
    props: ['onClick']
  });
</script>

这个时候,我希望点击的时候,执行 onClick,会执行对应的动作,这时只需要在对应的 ts 文件中写对应的代码逻辑即可:

@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'MyWidget' }))
export class MyWidgetManageWidget extends BaseElementWidget {
  // 获取当前页面所有的按钮
  @Widget.Reactive()
  public get modelActions() {
    return this.metadataRuntimeContext.model.modelActions || []
  }

  // 用来解析上下文表达式的,如果不需要,可以删除
  public executeCustomExpression<T>(
    parameters: Partial<ExpressionRunParam>,
    expression: string,
    errorValue?: T
  ): T | string | undefined {
    const scene = this.scene;
    return Expression.run(
      {
        activeRecords: parameters.activeRecords,
        rootRecord: parameters.rootRecord,
        openerRecord: parameters.openerRecord,
        scene: parameters.scene || scene,
        activeRecord: parameters.activeRecord
      } as ExpressionRunParam,
      expression,
      errorValue
    );
  }

  // 点击事件
  @Widget.Method()
  public onClick() {
    // 找到对应的按钮
    const action = this.modelActions.find((a) => a.label === '动作的显示名称');

     /**
    * 如果是服务端动作,就执行 executeServerAction
    */
    // executeServerAction(action, 参数对象) // 第二个参数是调用对应的接口传递的参数

 /**
    * 如果是跳转动作,就执行 executeViewAction
    */
    executeViewAction(action);

    /**
    * 如果跳转动作打开的视图需要通过id去查询数据,可以这样配置, 一般用于打开编辑、详情页
    */
    // executeViewAction(action, undefined, undefined, {id: 'xxxx'});

    /**
    * 如果跳转动作打开的视图需要需要额外的上下文参数,可以这样配置
    */
    // const _action = {
    //   ...action,
    //   context: {
    //     ...(action.context || {}),
    //     name: '123',
    //     code: '232'
    //   }
    // } as RuntimeViewAction
    // executeViewAction(_action, undefined, undefined, {id: 'xxxx'});

     /**
    * 如果跳转动作打开的视图需要上下文参数是在配置在对应动作上面,可以这样去解析上下文表达式
    */
    // const context = action.context || {}
    // const context = (action as RuntimeViewAction).context || {}
    // const parseContext = {}
    // const data = {} // 这是自己的数据源

    // Object.entries(context).forEach(([key, value]) => {
    //   if(typeof value === 'string') {
    //     parseContext[key] = this.executeCustomExpression({activeRecord: data, rootRecord: data,openerRecord: data}, value)
    //   }
    // })
    // executeViewAction({
    //   ...action,
    //   context:parseContext
    // } as RuntimeViewAction, undefined, undefined, {id: 'xxxx'});
  }

  public initialize(props: BaseElementWidgetProps): this {
    super.initialize(props);
    this.setComponent(MyVue);
    return this;
  }
}

以上我们就完成的自定义的视图如何触发界面设计器拖拽出来的按钮。

这是页面运行时的效果图

数式Oinone低代码-自定义的「视图、字段」调用界面设计器配置的按钮

数式Oinone低代码-自定义的「视图、字段」调用界面设计器配置的按钮

自定义字段

先自定义对应的字段,下面是我自定义的字段

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: ViewType.Form,
    ttype: ModelFieldType.String,
    widget: 'MyField'
  })
)
export class MyFieldWidget extends FormFieldWidget {
  // 获取当前页面所有的按钮
  @Widget.Reactive()
  public get modelActions() {
    return this.metadataRuntimeContext.model.modelActions || []
  }

  // 点击事件
  @Widget.Method()
  public onClick() {
    // 找到对应的按钮
    const action = this.modelActions.find((a) => a.label === '点击打开弹窗');

    // 执行按钮
    executeViewAction(action as any);
  }

  public initialize(props: BaseElementWidgetProps): this {
    super.initialize(props);
    this.setComponent(MyVue);
    return this;
  }
}
<template>
  <div @click="onClick">点击字段打开弹窗</div>
</template>

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

  export default defineComponent({
    props: ['onClick']
  });
</script>

其实不管是自定义的视图还是字段,里面执行动作的写法其实是一样的。

权限控制

如果当前系统用到了权限,那么我们在执行 action 时,需要判断当前 action 是否存在,因为当用户没有该 action 权限的时候,前端是获取不到对应的 action。

@Widget.Reactive()
public get hasMyAction() {
  return this.metadataRuntimeContext.model.modelActions.find(a => a.name === 'myAction')
}
<template> <div @click="onClick" v-if="hasMyAction">点击打开弹窗</div> </template>template

我们只需要定义一个计算属性,用来处理当前 action 是否存在,最后在 vue 模版里面使用 v-if 来控制是否显示。

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

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

(0)
汤乾华的头像汤乾华数式员工
上一篇 2023年11月8日 am10:59
下一篇 2023年11月8日 pm5:05

相关推荐

  • [前端]平台内置的基类

    前端平台内置了多个基类,允许开发者通过继承的方式来实现字段、视图以及动作。以下是一些常见的基类: 视图基类 通用视图基类 BaseElementWidget BaseElementWidget 是所有视图的通用基类,无论是何种视图,都可以继承这个基类。它封装了一系列属性和API,帮助开发者更轻松地创建各种视图组件。 表单类型的视图基类 BaseElementObjectViewWidget BaseElementObjectViewWidget 是表单视图的基类,它是BaseElementWidget的扩展。这个基类内部自动处理请求发起,以及数据刷新等一系列操作。 表格类型的视图基类 BaseElementListViewWidget BaseElementListViewWidget 是表格视图的基类,同样也是基于BaseElementWidget的扩展。它内部处理自动请求发起和数据刷新等操作,与BaseElementObjectViewWidget类似。 字段基类 表单字段基类 FormFieldWidget FormFieldWidget 是表单字段的基类,它封装了一系列属性和API,用于简化表单字段的开发。 表格字段基类 BaseTableFieldWidget BaseTableFieldWidget 是表格字段的基类,它封装了一系列属性和API,有助于开发者更轻松地创建表格字段。 动作基类 服务端动作基类 ServerActionWidget 跳转动作基类 RouterViewActionWidget 跳转动作基类(打开抽屉) DrawerViewActionWidget 跳转动作基类(打开抽屉) DrawerViewActionWidget 通过使用这些基类,开发者可以提高代码的可重用性和可维护性,从而更高效地开发前端应用。这些基类旨在帮助开发者更轻松地构建功能丰富的应用程序。

    2023年11月15日
    1.1K00
  • OioProvider详解

    OioProvider OioProvider是平台的初始化入口。 示例入口 main.ts import { VueOioProvider } from '@kunlun/dependencies'; VueOioProvider(); 网络请求/响应配置 http 平台统一使用apollo作为统一的http请求发起服务,并使用GraphQL协议作为前后端协议。 参考文档: apollo-client graphql 配置方式 VueOioProvider({ http?: OioHttpConfig }); OioHttpConfig /** * OioHttp配置 */ export interface OioHttpConfig { /** * base url */ url: string; /** * 拦截器配置 */ interceptor?: Partial<InterceptorOptions>; /** * 中间件配置(优先于拦截器) */ middleware?: NetworkMiddlewareHandler | NetworkMiddlewareHandler[]; } 内置拦截器可选项 InterceptorOptions /** * 拦截器可选项 */ export interface InterceptorOptions { /** * 网络错误拦截器 */ networkError: NetworkInterceptor; /** * 请求成功拦截器 (success) */ requestSuccess: NetworkInterceptor; /** * 重定向拦截器 (success) */ actionRedirect: NetworkInterceptor; /** * 登录重定向拦截器 (error) */ loginRedirect: NetworkInterceptor; /** * 请求错误拦截器 (error) */ requestError: NetworkInterceptor; /** * MessageHub拦截器 (success/error) */ messageHub: NetworkInterceptor; /** * 前置拦截器 */ beforeInterceptors: NetworkInterceptor | NetworkInterceptor[]; /** * 后置拦截器 */ afterInterceptors: NetworkInterceptor | NetworkInterceptor[]; } 内置拦截器执行顺序: beforeInterceptors:前置拦截器 networkError:网络错误 actionRedirect:重定向 requestSuccess 请求成功 loginRedirect:登录重定向 requestError:请求错误 messageHub:MessageHub afterInterceptors:后置拦截器 NetworkInterceptor /** * <h3>网络请求拦截器</h3> * <ul> * <li>拦截器将按照注册顺序依次执行</li> * <li>当任何一个拦截器返回false时,将中断拦截器执行</li> * <li>内置拦截器总是优先于自定义拦截器执行</li> * </ul> * */ export interface NetworkInterceptor { /** * 成功拦截 * @param response 响应结果 */ success?(response: IResponseResult): ReturnPromise<boolean>; /** * 错误拦截 * @param response 响应结果 */ error?(response: IResponseErrorResult): ReturnPromise<boolean>; } 自定义路由配置 router 配置方式 VueOioProvider({ router?: RouterPath[]…

    2023年11月6日
    1.2K00
  • 自定义mutation时出现校验不过时,如何排查

    场景描述 用户在自定义接口提供给前端调用时 @Action(displayName = "注册", bindingType = ViewTypeEnum.CUSTOM) public BaseResponse register(UserZhgl data) { //…逻辑 return result; } import java.io.Serializable; public class BaseResponse implements Serializable { private String code; private String msg; public BaseResponse() { } public BaseResponse(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } gql执行时出现报错 { "errors":[ { "message":"Validation error of type SubSelectionNotAllowed: Sub selection not allowed on leaf type Object of field register @ 'zhglMutation/register'", "locations":[ { "line":3, "column":3 } ], "extensions":{ "classification":"ValidationError" } } ] } 解决方案 1.返回对象不为空时,对象必须是模型,否则无法解析返回参数2.前端调用GQL异常时,可以用Insomnia工具对GQL进行测试,根据错误提示对GQL进行修改和排查3.GQL正常情况下,执行以后可根据后端日志进行错误排查

    2023年11月1日
    1.3K00
  • 前端打包文件上传到oss

    打包dist文件上传到oss教程 1: 确保boot工程里面安装了「webpack-aliyun-oss」依赖, 如果没有安装,可以手动安装下 2: package.json -> devDependencies -> {"webpack-aliyun-oss": "0.5.2"} 3: 在vue.config.js文件中添加对应的plugin // vue.config.js文件 // 新增代码 let BASE_URL = '/'; const { DEPLOY, OSS_REGION, OSS_DIST, OSS_URL, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, OSS_BUCKET } = process.env; const UNIQUE_KEY = Math.random(); switch (DEPLOY) { case 'online': BASE_URL = `${OSS_URL}${UNIQUE_KEY}/`; break; default: BASE_URL = '/'; } module.exports = { …原先的代码, publicPath: BASE_URL, plugins: [ …原先的插件, DEPLOY === 'online' ? new WebpackAliyunOss({ from: ['./dist/**/**', '!./dist/**/*.html'], // build目录下除html之外的所有文件 dist: `${OSS_DIST}/${UNIQUE_KEY}`, // oss上传目录 region: OSS_REGION, // 地区, 比如 oss-cn-hangzhou accessKeyId: OSS_ACCESS_KEY_ID, accessKeySecret: OSS_ACCESS_KEY_SECRET, bucket: OSS_BUCKET, timeout: 1200000, deleteOrigin: true, deleteEmptyDir: true, overwrite: true }) : () => {} ] } 4: 在vue.config.js同级目录下面新增「.env」文件, 写入对应的配置, 配置对应的值按照自己的oss配置来 DEPLOY=online OSS_BUCKET=xxx OSS_DIST=/oinone/product OSS_URL=https://xxxxxx/oinone/product/ OSS_REGION=oss-cn-hangzhou OSS_ACCESS_KEY_ID=xxxx OSS_ACCESS_KEY_SECRET=xxxx

    2023年11月1日
    1.0K00
  • oio-button 按钮

    主按钮:用于主行动点,一个操作区域只能有一个主按钮。 默认按钮:用于没有主次之分的一组行动点。 虚线按钮:常用于添加操作。 文本按钮:用于最次级的行动点。 链接按钮:一般用于链接,即导航至某位置。 以及四种状态属性与上面配合使用。 危险:删除/移动/修改权限等危险操作,一般需要二次确认。 禁用:行动点不可用的时候,一般需要文案解释。 加载中:用于异步操作等待反馈的时候,也可以避免多次提交。 API 按钮的属性说明如下: 属性 说明 类型 默认值 版本 block 将按钮宽度调整为其父宽度的选项 boolean false disabled 按钮失效状态 boolean false icon 设置按钮的图标类型 v-slot – loading 设置按钮载入状态 boolean | { delay: number } false type 设置按钮类型 primary | ghost | dashed | link | text | default default 事件 事件名称 说明 回调参数 版本 click 点击按钮时的回调 (event) => void 支持原生 button 的其他所有属性。

    2023年12月18日
    61700

Leave a Reply

登录后才能评论