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

介绍

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

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

代码示例

以下代码大部分场景只需要修改其中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

相关推荐

  • 列表视图、卡片视图切换

    在日常项目开发中,我们可能会遇到当前视图是个表格,通过某个操作按钮将它变成卡片的形式. 这篇文章将带大家实现这种功能。通过上面两张图片可以看到出来不管是表格还是卡片,它都在当前的视图里面,所以我们需要一个视图容器来包裹表格跟卡片,并且表格可以使用平台默认的表格渲染,我们只需要自定义卡片即可。 我们用资源模块下面的国家菜单来实现这么一个功能这是对应的URL: http://localhost:8080/page;module=resource;viewType=TABLE;model=resource.ResourceCountry;action=resource%23%E5%9B%BD%E5%AE%B6;scene=resource%23%E5%9B%BD%E5%AE%B6;target=OPEN_WINDOW;menu=%7B%22selectedKeys%22:%5B%22%E5%9B%BD%E5%AE%B6%22%5D,%22openKeys%22:%5B%22%E5%9C%B0%E5%9D%80%E5%BA%93%22,%22%E5%9C%B0%E5%8C%BA%22%5D%7D 源码下载 views 创建外层的视图容器 刚刚我们讲过,不管是表格还是卡片,它都在当前的视图里面,所以我们需要写一个视图容器来包裹它们,并且对应的容器里面允许拆入表格跟卡片,我们先创建TableWithCardViewWidget.ts // TableWithCardViewWidget.ts import { BaseElementWidget, SPI, Widget } from '@kunlun/dependencies'; import TableWithCardView from './TableWithCardView.vue'; enum ListViewType { TABLE = 'table', CARD = 'card' } @SPI.ClassFactory( BaseElementWidget.Token({ widget: 'TableWithCardViewWidget' }) ) export class TableWithCardViewWidget extends BaseElementWidget { @Widget.Reactive() private listViewType: ListViewType = ListViewType.TABLE; // 当前视图展示的类型,是展示卡片还是表格 public initialize(props) { if (!props.slotNames) { props.slotNames = ['tableWidget', 'cardWidget']; } super.initialize(props); this.setComponent(TableWithCardView); return this; } } 在TableWithCardViewWidget中的initialize函数中,我们定义了两个插槽: tableWidget、cardWidget,所以需要在对应的vue文件里面里接收这两个插槽 <template> <div class="list-view-wrapper"> <!– 表格插槽 –> <div style="height: 100%" v-if="listViewType === 'table'"> <slot name="tableWidget" /> </div> <!– 卡片插槽 –> <div v-if="listViewType === 'card'"> <slot name="cardWidget"></slot> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ props: ['listViewType', 'onChangeViewType'], inheritAttrs: false, setup(props, context) { const onChangeViewType = (listViewType) => { props.onChangeViewType(listViewType); }; } }); </script> 这样一来,我们就定义好了视图容器,接下来就是通过自定义layout的方式注册该容器 layout注册 import { registerLayout, ViewType } from '@kunlun/dependencies'; registerLayout( `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" cols="4" slot="search"/> </view> </pack> <pack widget="group" slot="tableGroup" style="position: relative"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="TableWithCardViewWidget"> <template slot="tableWidget"> <element widget="table" slot="table" datasource-provider="true"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" />…

    2024年10月16日
    1.8K01
  • [前端]平台内置的基类

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

    2023年11月15日
    1.0K00
  • oio-grid 栅格

    24 栅格系统。 <oio-row :gutter="24"> <oio-col :span="12"></oio-col> <oio-col :span="12"></oio-col> </oio-row> 概述 布局的栅格化系统,我们是基于行(row)和列(col)来定义信息区块的外部框架,以保证页面的每个区域能够稳健地排布起来。下面简单介绍一下它的工作原理: 通过\row\在水平方向建立一组\column\(简写 col) 你的内容应当放置于\col\内,并且,只有\col\可以作为\row\的直接元素 栅格系统中的列是指 1 到 24 的值来表示其跨越的范围。例如,三个等宽的列可以使用 \<a-col :span="8" />\ 来创建 如果一个\row\中的\col\总和超过 24,那么多余的\col\会作为一个整体另起一行排列 Flex 布局 我们的栅格化系统支持 Flex 布局,允许子元素在父节点内的水平对齐方式 – 居左、居中、居右、等宽排列、分散排列。子元素与子元素之间,支持顶部对齐、垂直居中对齐、底部对齐的方式。同时,支持使用 order 来定义元素的排列顺序。 Flex 布局是基于 24 栅格来定义每一个『盒子』的宽度,但不拘泥于栅格。 API Row 成员 说明 类型 默认值 align flex 布局下的垂直对齐方式:top middle bottom string top gutter 栅格间隔,可以写成像素值或支持响应式的对象写法来设置水平间隔 { xs: 8, sm: 16, md: 24}。或者使用数组形式同时设置 [水平间距, 垂直间距](1.5.0 后支持)。 number/object/array 0 justify flex 布局下的水平排列方式:start end center space-around space-between string start wrap 是否自动换行 boolean false Col 成员 说明 类型 默认值 版本 flex flex 布局填充 string|number – offset 栅格左侧的间隔格数,间隔内不可以有栅格 number 0 order 栅格顺序,flex 布局模式下有效 number 0 pull 栅格向左移动格数 number 0 push 栅格向右移动格数 number 0 span 栅格占位格数,为 0 时相当于 display: none number – xxxl ≥2000px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – xs <576px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – sm ≥576px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – md ≥768px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – lg ≥992px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – xl ≥1200px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object – xxl ≥1600px 响应式栅格,可为栅格数或一个包含其他属性的对象 number|object –

    2023年12月18日
    76900
  • 表格主题配置(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日
    89700
  • 表单页如何在服务端动作点击后让整个表单都处于loading状态

    介绍 在业务场景中,有时候由于提交的数据很多,导致服务端动作耗时较长,为了保证这个过程中表单内的字段不再能被编辑,我们可以通过自定义能力将整个表单区域处于loading状态 自定义动作组件代码 import { ActionType, ActionWidget, BaseElementViewWidget, BaseView, ClickResult, ServerActionWidget, SPI, Widget } from '@kunlun/dependencies'; @SPI.ClassFactory(ActionWidget.Token({ actionType: ActionType.Server })) class LoadingServerActionWidget extends ServerActionWidget { protected async clickAction(): Promise<ClickResult> { const baseView = Widget.select(this.rootHandle) as unknown as BaseView; if (!baseView) { return super.clickAction(); } const baseViewWidget = baseView.getChildrenInstance().find((a) => a instanceof BaseElementViewWidget) as unknown as BaseElementViewWidget; if (!baseViewWidget) { return super.clickAction(); } return new Promise((resolve, reject) => { try { baseViewWidget.load(async () => { const res = await super.clickAction(); resolve(res); }); } catch (e) { reject(false); } }); } } 本案例知识点 BaseElementWidget提供了load方法将继承了该class的元素渲染的区域做整体loading交互,等入参的函数处理完成后恢复正常状态,其实所有继承了ActionWidget的组件也提供了这个能力让按钮在执行函数中的时候处于loading状态, 每个组件都有一个全局唯一的handle值,所在根视图的rootHandle,组件可以用this.rootHandle通过Widget.Select方法查找到所在的根视图组件,从视图的实例化子元素里可以查找到具体的业务类型视图组件,如详情页的DetailWidget、表单页的FormWidget、表格页的TableWidget,拿到这些实例后就可以操作里面的属性和方法了

    2024年5月29日
    99500

Leave a Reply

登录后才能评论