如何自定义点击导出动作绑定指定模板

介绍

平台默认的导出会打开弹窗,然后在弹窗内的视图选择是用模板方式导出还是选字段导出,但是有时候有部分场景希望点击导出动作后直接进入导出流程,导出指定的某个模板,我们可以通过覆写打开弹窗的动作来实现该功能。

本文档参考了 表格页自定义按钮如何获取搜索区域的查询条件

代码示例

以下代码大部分场景只需要修改其中excelTplName更换模板即可,另外如何想增加复用性,还可以将该属性改为从元数据的配置中获取。

import {
  ActionType,
  BaseActionWidget,
  BaseElementListViewWidget,
  BooleanHelper,
  ClickResult,
  Condition,
  ExcelExportTask,
  FILE_MODULE_NAME,
  getSessionPath,
  GraphqlHelper,
  http,
  IQueryPageResult,
  ISort,
  queryDslWidget,
  ReturnPromise,
  RuntimeServerAction,
  ServerActionWidget,
  SPI,
  SubmitValue,
  SYSTEM_MODULE_NAME,
  translateValueByKey,
  UrlHelper,
  ViewActionTarget,
  Widget
} from '@kunlun/dependencies';
import { OioNotification } from '@kunlun/vue-ui-antd';

@SPI.ClassFactory(
  BaseActionWidget.Token({
    actionType: [ActionType.View],
    target: [ViewActionTarget.Dialog],
    model: 'ys0328.k2.Model0000000453',
    name: 'internalGotoListExportDialog'
  })
)
export class DemoExportActionWidget extends ServerActionWidget {
  /**
   * excel导出模板名称
   * @protected
   */
  protected excelTplName = '演示抽屉跳转链接导出';

  /**
   * 导出任务的模型编码
   * @protected
   */
  protected exportTaskModel = 'excelExportTask';

  /**
   * 导出任务的方法
   * @protected
   */
  protected exportTaskFun = 'createExportTask';

  /**
   *
   * 是否是同步导出
   */
  @Widget.Reactive()
  protected get syncExport() {
    return BooleanHelper.isTrue(this.getDsl().sync) || !true;
  }

  protected async executeAction(action: RuntimeServerAction, parameters: SubmitValue): Promise<ClickResult> {
    const workbookId = await this.getWorkbookId();
    if (!workbookId) {
      return false;
    }
    let task = {
      workbookDefinition: {
        id: workbookId
      }
    } as ExcelExportTask;

    // 从平台内置的方法获取搜索区域的条件
    const { condition } = this.getSearchRsqlAndQueryParams();

    // 排序规则
    let sortList = [] as ISort[];
    const baseViewWidget = Widget.select(this.rootHandle);
    const listViewWidget = queryDslWidget(baseViewWidget?.getChildrenInstance(), BaseElementListViewWidget);
    if (listViewWidget) {
      sortList = listViewWidget.sortList;
    }

    return this.export(task, condition, sortList);
  }

  protected getUploadBodyGql(id: string, condition: string | Condition, sortList: ISort[]) {
    let queryData = '{}';
    let rsql = condition;
    if (condition instanceof Condition) {
      const conditionBodyData = condition.getConditionBodyData();
      if (conditionBodyData && Object.keys(conditionBodyData).length) {
        queryData = GraphqlHelper.serializableObject(conditionBodyData);
      }
      rsql = condition.toString();
    }

    const sortListStr = (sortList || []).map((sort) => `{field: "${sort.sortField}", direction: ${sort.direction}}`).join(',');
    const sorts = sortListStr ? `sort: {orders: [${sortListStr}]}` : '';
    const conditionWrapper = condition
      ? `,conditionWrapper:{
            rsql: "${rsql}",
            queryData: "${queryData}"
            ${sorts}
          }`
      : '';
    const mutation = `mutation {
      ${this.exportTaskModel}Mutation {
        ${this.exportTaskFun}(data: {sync: ${this.syncExport}, workbookDefinition: {id: ${id}}${conditionWrapper}}) {
          id
          name
          state
        }
      }
    }`;

    return mutation;
  }

  protected async export(task: ExcelExportTask, condition: Condition | string, sortList: ISort[]) {
    let gql = await this.getUploadBodyGql(task.workbookDefinition!.id, condition, sortList);
    const variables = {
      path: getSessionPath()
    };
    let responseResult = false;
    if (this.syncExport) {
      window.open(
        UrlHelper.appendBasePath(
          `/pamirs/${FILE_MODULE_NAME}?query=${encodeURIComponent(gql)}&variables=${encodeURIComponent(
            JSON.stringify(variables)
          )}`
        ),
        '_blank'
      );
      responseResult = true;
    } else {
      const res = await http.mutate(SYSTEM_MODULE_NAME.FILE, gql, variables);
      const data = res.data[`${this.exportTaskModel}Mutation`][this.exportTaskFun] as any;
      responseResult = !!data.id;
    }
    if (responseResult) {
      OioNotification.success(this.translate('kunlun.common.success'));
      return true;
    }
    OioNotification.error(this.translate('kunlun.common.error'));
    return false;
  }

  protected async getWorkbookId(): ReturnPromise<string | undefined> {
    const model = this.model.model!;
    const gql = `{
  excelWorkbookDefinitionQuery {
    queryPage(
      page: {currentPage: 1, size: 1}
      queryWrapper: {rsql: "displayName=='${this.excelTplName}' and model == '${model}' and dataStatus == 'ENABLED' and type =in= ('IMPORT_EXPORT','EXPORT')"}
    ) {
      content {
        displayName
        id
      }
      totalPages
      totalElements
    }
  }
}
`;
    const variables = {
      path: getSessionPath()
    };
    const res = await http.query(SYSTEM_MODULE_NAME.FILE, gql, variables);
    const pageRes = res.data.excelWorkbookDefinitionQuery.queryPage as unknown as IQueryPageResult<any>;
    const id = pageRes.content?.[0]?.id;
    if (!id) {
      OioNotification.warning(translateValueByKey('没有找到匹配的导出模板'));
    }
    return id;
  }
}

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

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

(0)
nation的头像nation数式员工
上一篇 2024年10月8日 pm6:41
下一篇 2024年10月10日 am10:35

相关推荐

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

    自定义视图与界面设计器配置对接 在日常开发中,我们经常会遇到自定义视图的需求。自定义视图不仅需要与平台机制结合,还要实现与界面设计器中配置的字段和动作的无缝对接。本文将介绍如何将自定义视图与界面设计器中配置的字段和动作的无缝对接,实现字段和动作的渲染。 用大白话来讲就是:当前页面一部分是自定义的,一部分是设计器生成的 代码地址 目录 自定义表单视图与字段、动作的结合 自定义表格视图与字段、动作的结合 自定义表单视图与字段、动作的结合 首先需要在界面设计器配置好对应界面,虽然配置的页面样式跟期望展示的 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(); } });…

    2024年9月12日
    1.4K01
  • 前端自定义左树右表中的树

    在 oinone 平台中,提供了默认的左树右表的视图,用户可以通过界面设计器配置,默认的树视图不一定满足所有需求,尤其当需要自定义功能或复杂的交互时,我们可以通过自定义视图来实现更灵活的展现。 本文将带你一步步了解如何自定义左树右表视图中的树组件。 自定义树视图 1. 使用界面设计器配置视图 首先,我们需要通过界面设计器生成基础的左树右表视图。界面设计器允许用户根据不同需求进行拖拽配置,快速创建可视化界面。 配置完视图之后,我们可以重写左侧的树组件。Oinone 的默认树组件是 TableSearchTreeWidget,通过自定义的方式,我们可以实现更高级的功能。 2. 重写 TableSearchTreeWidget import { BaseElementWidget, SPI, TableSearchTreeWidget, ViewType } from '@kunlun/dependencies'; import CustomTableSearchTree from './CustomTableSearchTree.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: [ViewType.Table, ViewType.Form], widget: 'tree', model: 'resource.k2.Model0000000100' // 改成自己的模型 }) ) export class CustomTableSearchTreeWidget extends TableSearchTreeWidget { public initialize(props) { super.initialize(props); this.setComponent(CustomTableSearchTree); return this; } } 3. 定义 Vue 树组件 接下来,我们来实现 CustomTableSearchTree.vue 组件。这个组件将处理树的数据加载、节点选中等逻辑。你可以根据项目的需要修改其中的交互逻辑或 UI 设计。 <template> <a-tree :load-data="onLoadData" :tree-data="treeData" @select="onSelected" /> </template> <script lang="ts"> import { OioTreeNode, TreeUtils } from '@kunlun/dependencies'; import { computed, defineComponent } from 'vue'; export default defineComponent({ props: { rootNode: { type: Object }, loadData: { type: Function, required: true }, onSelected: { type: Function, required: true } }, setup(props) { // // 计算树的数据源,使用 TreeUtils 处理 const treeData = computed(() => { return TreeUtils.fillLoadMoreAction([…(props.rootNode?.children || [])]); }); // 异步加载子节点 const onLoadData = async (node) => { return await props.loadData(node.dataRef); }; // 处理节点选中事件 const onSelected = ( selectedKeys: string[], e: { nativeEvent: PointerEvent; node: { dataRef: OioTreeNode }; selected: boolean } ) => { props.onSelected?.(e.node.dataRef, e.selected); }; return { treeData, onLoadData, onSelected }; } }); </script> 4. 自定义…

    2024年10月21日
    2.8K00
  • 界面设计器 扩展字段的查询上下文

    默认情况下oinone平台对于查询条件,只提供的当前登录用户这一个配置,但是允许开发者扩展 开发者可以在前端代码的main.ts进行扩展 import { SessionContextOptions, ModelFieldType } from '@kunlun/dependencies'; const currentDeptOption = { ttype: ModelFieldType.String, value: '$#{currentDept}', displayName: '当前登录部门', label: '当前登录部门' }; SessionContextOptions.push(currentDeptOption as any); 加上上面的代码,然后再去界面设计器,我们就会发现,多了一个配置

    2023年11月8日
    1.9K00
  • 前端6.2.x升级指南

    升级背景 从 6.2.x 版本开始,平台底层核心代码已全面开源(设计器相关代码除外),本次升级涉及包名变更和配置调整,以下是详细的升级步骤 1. 更新 .npmrc 配置 修改当前项目根目录 .npmrc 文件,内容如下: registry=https://registry.npmmirror.com/ @oinone:registry=http://nexus.shushi.pro/repository/kunlun/ @kunlun:registry=http://nexus.shushi.pro/repository/kunlun/ shamefully-hoist=true legacy-peer-deps=true link-workspace-packages = true prefer-workspace-packages = true recursive-install = true lockfile=true 2. 包名变更 以前的包是 @kunlun/xxxx,现在的包名是@oinone/kunlun开头,所以需要全部替换修改。 旧包名 新包名 @kunlun/dependencies @oinone/kunlun-dependencies @kunlun/vue-ui-antd @oinone/kunlun-vue-ui-antd 3. 升级依赖版本 修改所有 package.json 中更新所有平台相关依赖: – "@kunlun/dependencies": "~5.7.0" + "@oinone/kunlun-dependencies": "~6.2.0" boot工程的package.json新增devDependencies依赖 "devDependencies": { "@types/lodash": "4.14.182", "@types/lodash-es": "4.17.6", ….. } 4. 更新代码引用 全局搜索并替换代码中的所有 @kunlun/ 为 @oinone/kunlun- 完整的 main.ts import 'ant-design-vue/dist/antd.min.css'; import 'element-plus/dist/index.css'; import '@oinone/kunlun-vue-ui-antd/dist/oinone-kunlun-vue-ui-antd.css'; import '@oinone/kunlun-vue-ui-el/dist/oinone-kunlun-vue-ui-el.css'; import '@oinone/kunlun-designer-common/dist/oinone-kunlun-designer-common.css'; import '@oinone/kunlun-model-designer/dist/oinone-kunlun-model-designer.css'; import '@oinone/kunlun-workflow-designer/dist/oinone-kunlun-workflow-designer.css'; import '@oinone/kunlun-workflow/dist/oinone-kunlun-workflow.css'; import '@oinone/kunlun-data-designer/dist/oinone-kunlun-data-designer.css'; import '@oinone/kunlun-microflow-designer/dist/oinone-kunlun-microflow-designer.css'; import 'reflect-metadata'; import ElementPlus from 'element-plus'; import { App } from 'vue'; import { VueOioProvider, RuntimeContextManager } from '@oinone/kunlun-dependencies'; // 设计器 import '@oinone/kunlun-ui-designer-dependencies'; import '@oinone/kunlun-print-designer-dependencies'; import '@oinone/kunlun-workflow'; import '@oinone/kunlun-workflow-designer'; import '@oinone/kunlun-model-designer'; import '@oinone/kunlun-data-designer'; import '@oinone/kunlun-data-designer-open-pc'; import '@oinone/kunlun-eip-dependencies'; import '@oinone/kunlun-ai-dependencies'; import '@oinone/kunlun-microflow-designer'; import '@oinone/kunlun-designer-common-ee'; // 下面三个依赖是当前工程的包,请根据实际场景修改 import '@ss/oinone'; import '@ss/admin-widget'; import '@ss/project'; VueOioProvider( { browser: { title: 'Oinone – 构你想象!', favicon: 'https://pamirs.oss-cn-hangzhou.aliyuncs.com/pamirs/image/default_favicon.ico' } }, [ () => { /// 获取当前vue实例 const app = RuntimeContextManager.createOrReplace().frameworkInstance as App; } ] ); 当配置全部修改完成后,重写安装依赖 # 清理旧依赖 pnpm clean # 安装新依赖 pnpm install

    2025年6月17日
    59400
  • 运行时上下文API文档(v4)

    运行时上下文 RuntimeContext export interface RuntimeContext<Framework = unknown> extends ContextNode<RuntimeContext<Framework>> { /** * 框架实例 */ frameworkInstance: Framework; /** * 路由路径 */ routers: RouterPath[]; /** * 运行时跳转动作(通过跳转动作创建的运行时上下文具备该属性) */ viewAction?: RuntimeViewAction; /** * 运行时字段(通过字段创建的运行时上下文具备该属性) */ field?: RuntimeModelField; /** * 运行时模块 */ module: RuntimeModule; /** * 运行时模型 */ model: RuntimeModel; /** * 运行时视图 */ view: RuntimeView; /** * 视图布局dsl,从运行时视图解析获得 */ viewLayout: DslDefinition | undefined; /** * 视图模板dsl,从运行时视图解析获得 */ viewDsl: DslDefinition | undefined; /** * 视图最终执行dsl,从运行时视图解析获得或根据布局dsl和模板dsl合并生成 */ viewTemplate: DslDefinition; /** * 扩展数据 */ extendData: Record<string, unknown>; /** * 获取模型 * @param model 模型编码 * @return 返回获取的模型和所在的运行时上下文 */ getModel(model: string): GetModelResult | undefined; /** * 获取模型字段 * @param name 字段名称 * @return 返回获取的模型字段和所在的运行时上下文 */ getModelField(name: string): GetModelFieldResult | undefined; /** * 创建字段上下文 * @param field 运行时模型字段 */ createFieldRuntimeContext(field: RuntimeModelField): RuntimeContext; /** * 深度解析模板,创建必要的子运行时上下文 */ deepResolve(): void; /** * 传输上下文参数到指定运行时上下文 * @param runtimeContext 运行时上下文 * @param clone 是否克隆; 默认: true */ transfer(runtimeContext: RuntimeContext, clone?: boolean); /** * 获取请求字段 */ getRequestModelFields(options?: GetRequestModelFieldsOptions): RequestModelField[]; /** * 获取默认值 */ getDefaultValue(): Promise<Record<string, unknown>>; /** * 获取初始值 */ getInitialValue(): Promise<Record<string, unknown>>; } 相关类型声明 export type GetModelResult = { model: RuntimeModel; runtimeContext: RuntimeContext;…

    2023年11月1日
    1.2K00

Leave a Reply

登录后才能评论