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

介绍

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

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

代码示例

以下代码大部分场景只需要修改其中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低代码应用平台体验

Like (0)
nation's avatarnation数式员工
Previous 2024年10月8日 pm6:41
Next 2024年10月10日 am10:35

相关推荐

  • 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
  • 【前端】登录页面扩展点

    登录页面扩展点 场景 1: 登录之前需要二次确认框2: 前端默认的错误提示允许被修改3: 后端返回的错误提示允许被修改4: 登录后跳转到自定义的页面 方案 前端默认错误可枚举 errorMessages: { loginEmpty: '用户名不能为空', passwordEmpty: '密码不能为空', picCodeEmpty: '图形验证码不能为空', phoneEmpty: '手机号不能为空', verificationCodeEmpty: '验证码不能为空', picCodeError: '图形验证码错误', inputVerificationCodeAlign: '请重新输入验证码' } 登录按钮添加拓展点beforeClick、afterClick 代码 新增一个ts文件,继承平台默认的LoginPageWidget @SPI.ClassFactory(RouterWidget.Token({ widget: 'Login' })) export class CustomLoginPageWidget extends LoginPageWidget { constructor() { super(); // 修改前端默认的错误文案 this.errorMessages.loginEmpty = '登录用户名不能为空'; } /** * 用来处理点击「登录」之前的事件,可以做二次确定或者其他的逻辑 * 只有return true,才会继续往下执行 */ public beforeClick(): Promise<Boolean | null | undefined> { return new Promise((resolve) => { Modal.confirm({ title: '提示', content: '是否登录?', onOk: () => { resolve(true); } }); }); } /** * * @param result 后端接口返回的数据 * * 用来处理「登录」接口调用后的逻辑,可以修改后端返回的错误文案,也可以自定义 * * 只有return true,才会执行默认的跳转事件 */ public afterClick(result): Promise<any | null | undefined> { // if(result.redirect) { // 自定义跳转 //return false //} if (result.errorCode === 20060023) { result.errorMsg = '手机号不对,请联系管理员'; } return result; } }

    2023年11月1日
    1.4K00
  • oio-input 输入框

    代码演示 <oio-input v-model:value="value"></oio-input> API Input 参数 说明 类型 默认值 版本 addonAfter 带标签的 input,设置后置标签 string|slot addonBefore 带标签的 input,设置前置标签 string|slot allowClear 可以点击清除图标删除内容 boolean defaultValue 输入框默认内容 string disabled 是否禁用状态,默认为 false boolean false maxlength 最大长度 number prefix 带有前缀图标的 input slot showCount 是否展示字数 boolean false suffix 带有后缀图标的 input slot type 声明 input 类型,同原生 input 标签的 type 属性,见:MDN(请直接使用 <a-textarea /> 代替 type="textarea")。 string text value(v-model:value) 输入框内容 string Input 事件 事件名称 说明 回调参数 update:value 输入框内容变化时的回调 function(e) pressEnter 按下回车的回调 function(e) Input.Search 代码演示 <oio-input-search v-model:value="value"></oio-input-search> Input.Search 事件 事件名称 说明 回调参数 search 点击搜索或按下回车键时的回调 function(value, event) 其余属性和 Input 一致。

    2023年12月18日
    2.9K00
  • 移动端端默认布局模板

    默认布局 表格视图(TABLE) <view type="TABLE"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field"> <xslot name="searchFields" slotSupport="field" /> </element> </view> <pack widget="group" class="oio-m-default-view-element" style="height: 100%;flex: 1;overflow-y: hidden;" wrapperStyle="height: 100%;box-sizing:border-box;"> <pack widget="row" style="height: 100%;"> <pack widget="col" mode="full" style="min-height: 234px;height: 100%;"> <element widget="table" slot="table" slotSupport="field" checkbox="false"> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" /> </element> </pack> </pack> </pack> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 表单视图(FORM) <view type="FORM"> <element widget="form" slot="form"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 画廊视图(GALLERY) 默认内嵌布局(inline=true) 内嵌表格视图(TABLE) 内嵌表单视图(FORM) 内嵌详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> </view>`

    2024年12月11日
    3.1K00
  • 【动作】-路由动作跳转后如何主动刷新页面数据

    介绍 当我们使用多tab组件的时候,如果一个viewAction已经打开了一个tab页,再次用该viewAction打开页面的时候,会发现不会根据路由上的业务参数(如详情和编辑页的id参数)主动刷新数据,这个时候可以通过以下方法解决该问题 // 该方法可以在进入新路由页面后刷新数据,推荐将该方法放到工具类 function refreshViewAction(action: any) { const onRefreshTabWithActive = (manager: MultiTabsManager, instance: MultiTabInstance) => { // 进入路由后刷新页面数据 manager.refresh(instance.key); manager.clearOnActive(onRefreshTabWithActive); }; MultiTabsManager.INSTANCE.onActive(onRefreshTabWithActive); executeViewAction(action); } 将原本调用executeViewAction的方法改为refreshViewAction 如果需要扩展executeViewAction的其他入参请自行拓展refreshViewAction的入参

    2024年6月18日
    2.2K00

Leave a Reply

Please Login to Comment