UNPKG

@nocobase/flow-engine

Version:

A standalone flow engine for NocoBase, managing workflows, models, and actions.

220 lines (195 loc) 6.17 kB
/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ import { observable } from '@formily/reactive'; import { AxiosRequestConfig } from 'axios'; import _ from 'lodash'; import { BaseRecordResource } from './baseRecordResource'; export class MultiRecordResource<TDataItem = any> extends BaseRecordResource<TDataItem[]> { protected _data = observable.ref<TDataItem[]>([]); protected _meta = observable.ref<Record<string, any>>({}); private refreshTimer: NodeJS.Timeout | null = null; protected createActionOptions = {}; protected updateActionOptions = {}; protected _refreshActionName = 'list'; // 请求配置 - 与 APIClient 接口保持一致 protected request = { // url: null as string | null, method: 'get' as string, params: { filter: {} as Record<string, any>, filterByTk: null as string | number | string[] | number[] | null, appends: [] as string[], fields: [] as string[], sort: null as string[] | null, except: null as string[] | null, whitelist: null as string[] | null, blacklist: null as string[] | null, page: 1 as number, pageSize: 20 as number, } as Record<string, any>, headers: {} as Record<string, any>, }; get supportsFilter() { return true; } setCreateActionOptions(options: AxiosRequestConfig) { this.createActionOptions = options; return this; } setUpdateActionOptions(options: AxiosRequestConfig) { this.updateActionOptions = options; return this; } setSelectedRows(selectedRows: TDataItem[]) { this.setMeta({ selectedRows }); return this; } getSelectedRows(): TDataItem[] { return this.getMeta('selectedRows') || []; } getCount(): number { return this.getMeta('count'); } setPage(page: number) { this.addRequestParameter('page', page); return this.setMeta({ page }); } getPage(): number { return this.getMeta('page'); } setPageSize(pageSize: number) { this.addRequestParameter('pageSize', pageSize); return this.setMeta({ pageSize }); } getPageSize(): number { return this.getMeta('pageSize'); } getTotalPage(): number { return this.getMeta('totalPage'); } getCell(rowIndex: number, columnKey: string): TDataItem | undefined { const data = this.getData(); return data?.[rowIndex]?.[columnKey]; } async next(): Promise<void> { this.request.params.page += 1; await this.refresh(); } async previous(): Promise<void> { if (this.request.params.page > 1) { this.request.params.page -= 1; await this.refresh(); } } async goto(page: number): Promise<void> { if (page > 0) { this.request.params.page = page; await this.refresh(); } } async create(data: TDataItem, options?: AxiosRequestConfig): Promise<void> { const config = this.mergeRequestConfig({ data }, this.createActionOptions, options); await this.runAction('create', config); await this.refresh(); } async get(filterByTk: any): Promise<TDataItem | undefined> { const options = { params: { filterByTk, }, }; const { data } = await this.runAction<TDataItem, any>('get', { ...options, }); return data; } async update(filterByTk: string | number, data: Partial<TDataItem>, options?: AxiosRequestConfig): Promise<void> { const config = this.mergeRequestConfig( { params: { filterByTk, }, data, }, this.updateActionOptions, options, ); await this.runAction('update', config); await this.refresh(); } async destroySelectedRows(): Promise<void> { const selectedRows = this.getSelectedRows(); if (selectedRows.length === 0) { throw new Error('No rows selected for deletion.'); } await this.destroy(selectedRows); } async destroy( filterByTk: string | number | string[] | number[] | TDataItem | TDataItem[], options?: AxiosRequestConfig, ): Promise<void> { const config = this.mergeRequestConfig( { params: { filterByTk: _.castArray(filterByTk).map((item) => { return typeof item === 'object' ? item['id'] : item; // TODO: ID 字段还需要根据实际情况更改 }), }, }, options, ); await this.runAction('destroy', config); await this.refresh(); } setItem(index: number, newDataItem: TDataItem) { const oldData = this.getData(); const newData = oldData.slice(); // 浅拷贝 newData[index] = { ...newDataItem }; this.setData(newData); } /** * 在同一个事件循环内多次调用 refresh 方法时,只有最后一次调用会生效。避免触发多次相同的接口请求。 * @returns */ async refresh(): Promise<void> { // 清除之前的定时器,确保只有最后一次调用生效 if (this.refreshTimer) { clearTimeout(this.refreshTimer); } // 设置新的定时器,在下一个事件循环执行 return new Promise<void>((resolve, reject) => { this.refreshTimer = setTimeout(async () => { try { this.clearError(); this.loading = true; const { data, meta } = await this.runAction<TDataItem[], any>(this._refreshActionName, { method: 'get', ...this.getRefreshRequestOptions(), }); this.setData(data).setMeta(meta); if (meta?.page) { this.setPage(meta.page); } if (meta?.pageSize) { this.setPageSize(meta.pageSize); } this.emit('refresh'); this.loading = false; resolve(); } catch (error) { this.setError(error); reject(error instanceof Error ? error : new Error(String(error))); } finally { this.refreshTimer = null; this.loading = false; } }); }); } }