@nocobase/flow-engine
Version:
A standalone flow engine for NocoBase, managing workflows, models, and actions.
123 lines (102 loc) • 3.92 kB
text/typescript
/**
* 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 { FlowEngineContext } from '../flowContext';
interface ViewParam {
/** 视图唯一标识符,一般为某个 Model 实例的 uid */
viewUid?: string;
/** 标签页唯一标识符 */
tabUid?: string;
/** 弹窗记录的 id */
filterByTk?: string;
/** source Id */
sourceId?: string;
}
/**
* 将 ViewParam 数组转换为 pathname
*
* @param viewParams - ViewParam 数组
* @returns 生成的 pathname
*
* @example
* ```typescript
* generatePathnameFromViewParams([{ viewUid: 'xxx' }]) // '/admin/xxx'
* generatePathnameFromViewParams([{ viewUid: 'xxx', tabUid: 'yyy' }]) // '/admin/xxx/tab/yyy'
* generatePathnameFromViewParams([{ viewUid: 'xxx' }, { viewUid: 'yyy' }]) // '/admin/xxx/view/yyy'
* ```
*/
export function generatePathnameFromViewParams(viewParams: ViewParam[]): string {
if (!viewParams || viewParams.length === 0) {
return '/admin';
}
const segments = ['admin'];
viewParams.forEach((viewParam, index) => {
// 如果不是第一个视图,添加 'view' 关键字
if (index > 0) {
segments.push('view');
}
// 添加视图 UID
segments.push(viewParam.viewUid);
// 添加参数
if (viewParam.tabUid) {
segments.push('tab', viewParam.tabUid);
}
if (viewParam.filterByTk) {
segments.push('filterbytk', viewParam.filterByTk);
}
if (viewParam.sourceId) {
segments.push('sourceid', viewParam.sourceId);
}
});
return '/' + segments.join('/');
}
export class ViewNavigation {
viewStack: ViewParam[];
ctx: FlowEngineContext;
constructor(ctx: FlowEngineContext, viewParams: ViewParam[]) {
this.viewStack = [...viewParams];
this.ctx = ctx;
}
changeTo(viewParam: ViewParam) {
// 1. 根据传入的参数,改变当前视图的参数。当前视图的参数是 viewStack 中最后一个元素
if (this.viewStack.length === 0) {
this.viewStack.push(viewParam);
} else {
this.viewStack[this.viewStack.length - 1] = { ...this.viewStack[this.viewStack.length - 1], ...viewParam };
}
// 2. 根据 viewStack 生成新的 pathname
const newPathname = generatePathnameFromViewParams(this.viewStack);
// 3. 触发一次跳转。使用 replace 的方式
this.ctx.router.navigate(newPathname, { replace: true });
}
navigateTo(viewParam: ViewParam, opts?: { replace?: boolean; state?: any }) {
// 1. 基于当前 viewStack 生成一个 pathname
// 2. 将当前传入的参数转为 path string
const newViewPathname = generatePathnameFromViewParams([...this.viewStack, viewParam]);
// 3. 与 pathname 拼接成新的 pathname(这里直接使用新生成的 pathname)
const newPathname = newViewPathname;
// 4. 判断新的 pathname 是否与当前 location.pathname 的结尾一致。防止出现重复路径
// 不用 this.ctx.route.pathname 是因为它的值可能不是最新的,会导致判断失误
if (location.pathname.endsWith(newPathname)) {
return this.ctx.router.navigate(-1); // 避免点击按钮没反应的问题
}
// 5. 如果新的 pathname 与当前 ctx.route.pathname 不同,则触发一次跳转。使用 push 的方式
this.ctx.router.navigate(newPathname, opts);
// 6. 当 viewStack 为空时,把当前参数 push 到 viewStack 中
if (this.viewStack.length === 0) {
this.viewStack.push(viewParam);
}
}
back() {
const pathname = generatePathnameFromViewParams(this.viewStack);
// 防止重复触发返回操作
if (location.pathname.endsWith(pathname)) {
this.ctx.router.navigate(-1);
}
}
}