mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-02 11:12:20 +08:00
feat: support extends toolbar setting icon
This commit is contained in:
parent
2e27ee90c0
commit
a6d061a192
@ -0,0 +1,224 @@
|
|||||||
|
/**
|
||||||
|
* 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 React, { useCallback } from 'react';
|
||||||
|
import { Dropdown, Modal, App } from 'antd';
|
||||||
|
import type { MenuProps } from 'antd';
|
||||||
|
import {
|
||||||
|
SettingOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
ExclamationCircleOutlined,
|
||||||
|
MenuOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { FlowModel } from '../../../../models';
|
||||||
|
import { ActionStepDefinition } from '../../../../types';
|
||||||
|
import { openStepSettings } from './StepSettings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的设置菜单图标组件
|
||||||
|
* 提供原有的配置菜单功能
|
||||||
|
*/
|
||||||
|
export const DefaultSettingsIcon: React.FC<{
|
||||||
|
model: FlowModel;
|
||||||
|
showDeleteButton?: boolean;
|
||||||
|
showCopyUidButton?: boolean;
|
||||||
|
[key: string]: any; // 允许额外的 props
|
||||||
|
}> = ({ model, showDeleteButton = true, showCopyUidButton = true }) => {
|
||||||
|
const { message } = App.useApp();
|
||||||
|
|
||||||
|
const handleMenuClick = useCallback(
|
||||||
|
({ key }: { key: string }) => {
|
||||||
|
if (key === 'copy-uid') {
|
||||||
|
// 处理复制 uid 操作
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(model.uid)
|
||||||
|
.then(() => {
|
||||||
|
message.success('UID 已复制到剪贴板');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('复制失败:', error);
|
||||||
|
message.error('复制失败,请重试');
|
||||||
|
});
|
||||||
|
} else if (key === 'delete') {
|
||||||
|
// 处理删除操作
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
icon: <ExclamationCircleOutlined />,
|
||||||
|
content: '确定要删除此项吗?此操作不可撤销。',
|
||||||
|
okText: '确认删除',
|
||||||
|
okType: 'primary',
|
||||||
|
cancelText: '取消',
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
await model.destroy();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除操作失败:', error);
|
||||||
|
Modal.error({
|
||||||
|
title: '删除失败',
|
||||||
|
content: '删除操作失败,请检查控制台获取详细信息。',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 处理step配置,key格式为 "flowKey:stepKey"
|
||||||
|
const [flowKey, stepKey] = key.split(':');
|
||||||
|
try {
|
||||||
|
openStepSettings({
|
||||||
|
model,
|
||||||
|
flowKey,
|
||||||
|
stepKey,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// 用户取消或出错,静默处理
|
||||||
|
console.log('配置弹窗已取消或出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[model, message],
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取可配置的flows和steps
|
||||||
|
const getConfigurableFlowsAndSteps = useCallback(() => {
|
||||||
|
try {
|
||||||
|
const ModelClass = model.constructor as typeof FlowModel;
|
||||||
|
const flows = ModelClass.getFlows();
|
||||||
|
|
||||||
|
const flowsArray = Array.from(flows.values());
|
||||||
|
|
||||||
|
return flowsArray
|
||||||
|
.map((flow) => {
|
||||||
|
const configurableSteps = Object.entries(flow.steps)
|
||||||
|
.map(([stepKey, stepDefinition]) => {
|
||||||
|
const actionStep = stepDefinition as ActionStepDefinition;
|
||||||
|
|
||||||
|
// 如果步骤设置了 hideInSettings: true,则跳过此步骤
|
||||||
|
if (actionStep.hideInSettings) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从step获取uiSchema(如果存在)
|
||||||
|
const stepUiSchema = actionStep.uiSchema || {};
|
||||||
|
|
||||||
|
// 如果step使用了action,也获取action的uiSchema
|
||||||
|
let actionUiSchema = {};
|
||||||
|
if (actionStep.use) {
|
||||||
|
const action = model.flowEngine?.getAction?.(actionStep.use);
|
||||||
|
if (action && action.uiSchema) {
|
||||||
|
actionUiSchema = action.uiSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并uiSchema,确保step的uiSchema优先级更高
|
||||||
|
const mergedUiSchema = { ...actionUiSchema };
|
||||||
|
|
||||||
|
// 将stepUiSchema中的字段合并到mergedUiSchema
|
||||||
|
Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
|
||||||
|
if (mergedUiSchema[fieldKey]) {
|
||||||
|
mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
|
||||||
|
} else {
|
||||||
|
mergedUiSchema[fieldKey] = schema;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果没有可配置的UI Schema,返回null
|
||||||
|
if (Object.keys(mergedUiSchema).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
stepKey,
|
||||||
|
step: actionStep,
|
||||||
|
uiSchema: mergedUiSchema,
|
||||||
|
title: actionStep.title || stepKey,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
return configurableSteps.length > 0 ? { flow, steps: configurableSteps } : null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[DefaultSettingsIcon] 获取可配置flows失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [model]);
|
||||||
|
|
||||||
|
const configurableFlowsAndSteps = getConfigurableFlowsAndSteps();
|
||||||
|
|
||||||
|
// 如果没有可配置的flows且不显示删除按钮和复制UID按钮,不显示菜单
|
||||||
|
if (configurableFlowsAndSteps.length === 0 && !showDeleteButton && !showCopyUidButton) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建菜单项
|
||||||
|
const menuItems: MenuProps['items'] = [];
|
||||||
|
|
||||||
|
// 添加flows和steps配置项
|
||||||
|
if (configurableFlowsAndSteps.length > 0) {
|
||||||
|
configurableFlowsAndSteps.forEach(({ flow, steps }) => {
|
||||||
|
// 始终按flow分组显示
|
||||||
|
menuItems.push({
|
||||||
|
key: `flow-group-${flow.key}`,
|
||||||
|
label: flow.title || flow.key,
|
||||||
|
type: 'group',
|
||||||
|
});
|
||||||
|
|
||||||
|
steps.forEach((stepInfo) => {
|
||||||
|
menuItems.push({
|
||||||
|
key: `${flow.key}:${stepInfo.stepKey}`,
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
label: stepInfo.title,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加分割线、复制 uid 和删除按钮
|
||||||
|
if (showCopyUidButton || showDeleteButton) {
|
||||||
|
// 如果有flows配置项,添加分割线
|
||||||
|
if (configurableFlowsAndSteps.length > 0) {
|
||||||
|
menuItems.push({
|
||||||
|
type: 'divider' as const,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加复制 uid 按钮
|
||||||
|
if (showCopyUidButton) {
|
||||||
|
menuItems.push({
|
||||||
|
key: 'copy-uid',
|
||||||
|
icon: <CopyOutlined />,
|
||||||
|
label: '复制 UID',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加删除按钮
|
||||||
|
if (showDeleteButton) {
|
||||||
|
menuItems.push({
|
||||||
|
key: 'delete',
|
||||||
|
icon: <DeleteOutlined />,
|
||||||
|
label: '删除',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
menu={{
|
||||||
|
items: menuItems,
|
||||||
|
onClick: handleMenuClick,
|
||||||
|
}}
|
||||||
|
trigger={['hover']}
|
||||||
|
placement="bottomRight"
|
||||||
|
>
|
||||||
|
<MenuOutlined role="button" aria-label="flows-settings" style={{ cursor: 'pointer', fontSize: 12 }} />
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
};
|
@ -8,22 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
import { Alert, Modal, Space, Dropdown, App } from 'antd';
|
import { Alert, Space } from 'antd';
|
||||||
import type { MenuProps } from 'antd';
|
|
||||||
import {
|
|
||||||
SettingOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
ExclamationCircleOutlined,
|
|
||||||
MenuOutlined,
|
|
||||||
CopyOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { observer } from '@formily/react';
|
import { observer } from '@formily/react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { FlowModel } from '../../../../models';
|
import { FlowModel } from '../../../../models';
|
||||||
import { ActionStepDefinition } from '../../../../types';
|
import { ToolbarItemConfig } from '../../../../types';
|
||||||
import { useFlowModelById } from '../../../../hooks';
|
import { useFlowModelById } from '../../../../hooks';
|
||||||
import { useFlowEngine } from '../../../../provider';
|
import { useFlowEngine } from '../../../../provider';
|
||||||
import { openStepSettings } from './StepSettings';
|
import { FlowEngine } from '../../../../flowEngine';
|
||||||
|
|
||||||
// 检测DOM中直接子元素是否包含button元素的辅助函数
|
// 检测DOM中直接子元素是否包含button元素的辅助函数
|
||||||
const detectButtonInDOM = (container: HTMLElement): boolean => {
|
const detectButtonInDOM = (container: HTMLElement): boolean => {
|
||||||
@ -42,6 +34,41 @@ const detectButtonInDOM = (container: HTMLElement): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 渲染工具栏项目的辅助函数
|
||||||
|
const renderToolbarItems = (
|
||||||
|
model: FlowModel,
|
||||||
|
showDeleteButton: boolean,
|
||||||
|
showCopyUidButton: boolean,
|
||||||
|
flowEngine: FlowEngine,
|
||||||
|
) => {
|
||||||
|
const toolbarItems = flowEngine?.flowSettings?.getToolbarItems?.() || [];
|
||||||
|
|
||||||
|
return toolbarItems
|
||||||
|
.filter((itemConfig: ToolbarItemConfig) => {
|
||||||
|
// 检查项目是否应该显示
|
||||||
|
return itemConfig.visible ? itemConfig.visible(model) : true;
|
||||||
|
})
|
||||||
|
.map((itemConfig: ToolbarItemConfig) => {
|
||||||
|
// 渲染项目组件
|
||||||
|
const ItemComponent = itemConfig.component;
|
||||||
|
|
||||||
|
// 对于默认设置项目,传递额外的 props
|
||||||
|
if (itemConfig.key === 'settings-menu') {
|
||||||
|
return (
|
||||||
|
<ItemComponent
|
||||||
|
key={itemConfig.key}
|
||||||
|
model={model}
|
||||||
|
showDeleteButton={showDeleteButton}
|
||||||
|
showCopyUidButton={showCopyUidButton}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他项目只传递 model
|
||||||
|
return <ItemComponent key={itemConfig.key} model={model} />;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 使用与 NocoBase 一致的悬浮工具栏样式
|
// 使用与 NocoBase 一致的悬浮工具栏样式
|
||||||
const floatContainerStyles = ({ showBackground, showBorder }) => css`
|
const floatContainerStyles = ({ showBackground, showBorder }) => css`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -196,7 +223,7 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|||||||
const [hideMenu, setHideMenu] = useState<boolean>(false);
|
const [hideMenu, setHideMenu] = useState<boolean>(false);
|
||||||
const [hasButton, setHasButton] = useState<boolean>(false);
|
const [hasButton, setHasButton] = useState<boolean>(false);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const { message } = App.useApp();
|
const flowEngine = useFlowEngine();
|
||||||
|
|
||||||
// 检测DOM中是否包含button元素
|
// 检测DOM中是否包含button元素
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -241,58 +268,6 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleMenuClick = useCallback(
|
|
||||||
({ key }: { key: string }) => {
|
|
||||||
if (key === 'copy-uid') {
|
|
||||||
// 处理复制 uid 操作
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(model.uid)
|
|
||||||
.then(() => {
|
|
||||||
message.success('UID 已复制到剪贴板');
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('复制失败:', error);
|
|
||||||
message.error('复制失败,请重试');
|
|
||||||
});
|
|
||||||
} else if (key === 'delete') {
|
|
||||||
// 处理删除操作
|
|
||||||
Modal.confirm({
|
|
||||||
title: '确认删除',
|
|
||||||
icon: <ExclamationCircleOutlined />,
|
|
||||||
content: '确定要删除此项吗?此操作不可撤销。',
|
|
||||||
okText: '确认删除',
|
|
||||||
okType: 'primary',
|
|
||||||
cancelText: '取消',
|
|
||||||
async onOk() {
|
|
||||||
try {
|
|
||||||
await model.destroy();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('删除操作失败:', error);
|
|
||||||
Modal.error({
|
|
||||||
title: '删除失败',
|
|
||||||
content: '删除操作失败,请检查控制台获取详细信息。',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 处理step配置,key格式为 "flowKey:stepKey"
|
|
||||||
const [flowKey, stepKey] = key.split(':');
|
|
||||||
try {
|
|
||||||
openStepSettings({
|
|
||||||
model,
|
|
||||||
flowKey,
|
|
||||||
stepKey,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// 用户取消或出错,静默处理
|
|
||||||
console.log('配置弹窗已取消或出错:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[model, message],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return <Alert message="提供的模型无效" type="error" />;
|
return <Alert message="提供的模型无效" type="error" />;
|
||||||
}
|
}
|
||||||
@ -302,130 +277,6 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取可配置的flows和steps
|
|
||||||
const getConfigurableFlowsAndSteps = useCallback(() => {
|
|
||||||
try {
|
|
||||||
const ModelClass = model.constructor as typeof FlowModel;
|
|
||||||
const flows = ModelClass.getFlows();
|
|
||||||
|
|
||||||
const flowsArray = Array.from(flows.values());
|
|
||||||
|
|
||||||
return flowsArray
|
|
||||||
.map((flow) => {
|
|
||||||
const configurableSteps = Object.entries(flow.steps)
|
|
||||||
.map(([stepKey, stepDefinition]) => {
|
|
||||||
const actionStep = stepDefinition as ActionStepDefinition;
|
|
||||||
|
|
||||||
// 如果步骤设置了 hideInSettings: true,则跳过此步骤
|
|
||||||
if (actionStep.hideInSettings) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从step获取uiSchema(如果存在)
|
|
||||||
const stepUiSchema = actionStep.uiSchema || {};
|
|
||||||
|
|
||||||
// 如果step使用了action,也获取action的uiSchema
|
|
||||||
let actionUiSchema = {};
|
|
||||||
if (actionStep.use) {
|
|
||||||
const action = model.flowEngine?.getAction?.(actionStep.use);
|
|
||||||
if (action && action.uiSchema) {
|
|
||||||
actionUiSchema = action.uiSchema;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合并uiSchema,确保step的uiSchema优先级更高
|
|
||||||
const mergedUiSchema = { ...actionUiSchema };
|
|
||||||
|
|
||||||
// 将stepUiSchema中的字段合并到mergedUiSchema
|
|
||||||
Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
|
|
||||||
if (mergedUiSchema[fieldKey]) {
|
|
||||||
mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
|
|
||||||
} else {
|
|
||||||
mergedUiSchema[fieldKey] = schema;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 如果没有可配置的UI Schema,返回null
|
|
||||||
if (Object.keys(mergedUiSchema).length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
stepKey,
|
|
||||||
step: actionStep,
|
|
||||||
uiSchema: mergedUiSchema,
|
|
||||||
title: actionStep.title || stepKey,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
return configurableSteps.length > 0 ? { flow, steps: configurableSteps } : null;
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('[FlowsFloatContextMenu] 获取可配置flows失败:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}, [model]);
|
|
||||||
|
|
||||||
const configurableFlowsAndSteps = getConfigurableFlowsAndSteps();
|
|
||||||
|
|
||||||
// 如果没有可配置的flows且不显示删除按钮和复制UID按钮,直接返回children
|
|
||||||
if (configurableFlowsAndSteps.length === 0 && !showDeleteButton && !showCopyUidButton) {
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建菜单项 - 使用与 FlowsContextMenu 相同的逻辑
|
|
||||||
const menuItems: MenuProps['items'] = [];
|
|
||||||
|
|
||||||
// 添加flows和steps配置项
|
|
||||||
if (configurableFlowsAndSteps.length > 0) {
|
|
||||||
configurableFlowsAndSteps.forEach(({ flow, steps }) => {
|
|
||||||
// 始终按flow分组显示
|
|
||||||
menuItems.push({
|
|
||||||
key: `flow-group-${flow.key}`,
|
|
||||||
label: flow.title || flow.key,
|
|
||||||
type: 'group',
|
|
||||||
});
|
|
||||||
|
|
||||||
steps.forEach((stepInfo) => {
|
|
||||||
menuItems.push({
|
|
||||||
key: `${flow.key}:${stepInfo.stepKey}`,
|
|
||||||
icon: <SettingOutlined />,
|
|
||||||
label: stepInfo.title,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加分割线、复制 uid 和删除按钮
|
|
||||||
if (showCopyUidButton || showDeleteButton) {
|
|
||||||
// 如果有flows配置项,添加分割线
|
|
||||||
if (configurableFlowsAndSteps.length > 0) {
|
|
||||||
menuItems.push({
|
|
||||||
type: 'divider' as const,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加复制 uid 按钮
|
|
||||||
if (showCopyUidButton) {
|
|
||||||
menuItems.push({
|
|
||||||
key: 'copy-uid',
|
|
||||||
icon: <CopyOutlined />,
|
|
||||||
label: '复制 UID',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加删除按钮
|
|
||||||
if (showDeleteButton) {
|
|
||||||
menuItems.push({
|
|
||||||
key: 'delete',
|
|
||||||
icon: <DeleteOutlined />,
|
|
||||||
label: '删除',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
@ -442,16 +293,7 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|||||||
<div className="general-schema-designer">
|
<div className="general-schema-designer">
|
||||||
<div className="general-schema-designer-icons">
|
<div className="general-schema-designer-icons">
|
||||||
<Space size={3} align="center">
|
<Space size={3} align="center">
|
||||||
<Dropdown
|
{renderToolbarItems(model, showDeleteButton, showCopyUidButton, flowEngine)}
|
||||||
menu={{
|
|
||||||
items: menuItems,
|
|
||||||
onClick: handleMenuClick,
|
|
||||||
}}
|
|
||||||
trigger={['hover']}
|
|
||||||
placement="bottomRight"
|
|
||||||
>
|
|
||||||
<MenuOutlined role="button" aria-label="flows-settings" style={{ cursor: 'pointer', fontSize: 12 }} />
|
|
||||||
</Dropdown>
|
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 React from 'react';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import { openStepSettingsDrawer } from '../StepSettingsDrawer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最小化的抽屉测试
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 创建一个模拟的模型对象
|
|
||||||
const mockModel = {
|
|
||||||
uid: 'test-model',
|
|
||||||
flowEngine: {
|
|
||||||
context: {},
|
|
||||||
flowSettings: {
|
|
||||||
components: {},
|
|
||||||
scopes: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getFlow: (flowKey: string) => {
|
|
||||||
if (flowKey === 'testFlow') {
|
|
||||||
return {
|
|
||||||
key: 'testFlow',
|
|
||||||
title: '测试流程',
|
|
||||||
steps: {
|
|
||||||
drawerStep: {
|
|
||||||
title: '抽屉步骤',
|
|
||||||
settingMode: 'drawer',
|
|
||||||
uiSchema: {
|
|
||||||
title: {
|
|
||||||
type: 'string',
|
|
||||||
title: '标题',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: 'string',
|
|
||||||
title: '描述',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input.TextArea',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
title: '默认标题',
|
|
||||||
description: '默认描述',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
getStepParams: (flowKey: string, stepKey: string) => {
|
|
||||||
return {
|
|
||||||
title: '当前标题',
|
|
||||||
description: '当前描述',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setStepParams: (flowKey: string, stepKey: string, params: any) => {
|
|
||||||
console.log('设置步骤参数:', { flowKey, stepKey, params });
|
|
||||||
},
|
|
||||||
save: async () => {
|
|
||||||
console.log('保存模型');
|
|
||||||
return Promise.resolve();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const MinimalDrawerTest: React.FC = () => {
|
|
||||||
const handleOpenDrawer = async () => {
|
|
||||||
try {
|
|
||||||
const result = await openStepSettingsDrawer({
|
|
||||||
model: mockModel,
|
|
||||||
flowKey: 'testFlow',
|
|
||||||
stepKey: 'drawerStep',
|
|
||||||
drawerWidth: 600,
|
|
||||||
drawerTitle: '测试抽屉配置',
|
|
||||||
});
|
|
||||||
console.log('抽屉配置结果:', result);
|
|
||||||
} catch (error) {
|
|
||||||
console.log('抽屉配置取消或出错:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ padding: 20 }}>
|
|
||||||
<h3>最小化抽屉测试</h3>
|
|
||||||
<p>这是一个最小化的抽屉测试,用于验证基本功能。</p>
|
|
||||||
<Button type="primary" onClick={handleOpenDrawer}>
|
|
||||||
打开抽屉配置
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MinimalDrawerTest;
|
|
@ -1,110 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 React from 'react';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import { FlowModel } from '../../../../../models';
|
|
||||||
import { openStepSettings } from '../StepSettings';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 简单的抽屉测试示例
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 创建一个简单的测试模型
|
|
||||||
class SimpleTestModel extends FlowModel {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div style={{ padding: 20, border: '1px solid #ccc', borderRadius: 4 }}>
|
|
||||||
<h3>简单抽屉测试</h3>
|
|
||||||
<p>点击下面的按钮测试抽屉配置功能</p>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
openStepSettings({
|
|
||||||
model: this,
|
|
||||||
flowKey: 'testFlow',
|
|
||||||
stepKey: 'drawerStep',
|
|
||||||
})
|
|
||||||
.then((values) => {
|
|
||||||
console.log('保存的配置:', values);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log('用户取消或出错:', error);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
打开抽屉配置
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册简单的测试流程
|
|
||||||
SimpleTestModel.registerFlow({
|
|
||||||
key: 'testFlow',
|
|
||||||
title: '测试流程',
|
|
||||||
steps: {
|
|
||||||
drawerStep: {
|
|
||||||
title: '抽屉步骤',
|
|
||||||
settingMode: 'drawer', // 使用抽屉模式
|
|
||||||
uiSchema: {
|
|
||||||
title: {
|
|
||||||
type: 'string',
|
|
||||||
title: '标题',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-component-props': {
|
|
||||||
placeholder: '请输入标题',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: 'string',
|
|
||||||
title: '描述',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input.TextArea',
|
|
||||||
'x-component-props': {
|
|
||||||
placeholder: '请输入描述',
|
|
||||||
rows: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: 'boolean',
|
|
||||||
title: '启用',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Switch',
|
|
||||||
},
|
|
||||||
priority: {
|
|
||||||
type: 'string',
|
|
||||||
title: '优先级',
|
|
||||||
enum: [
|
|
||||||
{ label: '低', value: 'low' },
|
|
||||||
{ label: '中', value: 'medium' },
|
|
||||||
{ label: '高', value: 'high' },
|
|
||||||
],
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
title: '默认标题',
|
|
||||||
description: '默认描述',
|
|
||||||
enabled: true,
|
|
||||||
priority: 'medium',
|
|
||||||
},
|
|
||||||
// 简单的处理函数
|
|
||||||
handler: (ctx, params) => {
|
|
||||||
console.log('执行抽屉步骤:', params);
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export { SimpleTestModel };
|
|
@ -1,151 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 React from 'react';
|
|
||||||
import { Button, Space } from 'antd';
|
|
||||||
import { FlowModel } from '../../../../../models';
|
|
||||||
import { openStepSettings, getStepSettingMode } from '../StepSettings';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 示例:展示如何使用 settingMode: 'drawer' 配置
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 创建一个示例模型类
|
|
||||||
class ExampleModel extends FlowModel {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div style={{ padding: 20, border: '1px solid #ccc', borderRadius: 4 }}>
|
|
||||||
<h3>示例模型</h3>
|
|
||||||
<p>这是一个配置了不同 settingMode 的示例模型</p>
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
openStepSettings({
|
|
||||||
model: this,
|
|
||||||
flowKey: 'exampleFlow',
|
|
||||||
stepKey: 'dialogStep',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
打开对话框配置 (Dialog)
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
openStepSettings({
|
|
||||||
model: this,
|
|
||||||
flowKey: 'exampleFlow',
|
|
||||||
stepKey: 'drawerStep',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
打开抽屉配置 (Drawer)
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
<div style={{ marginTop: 10 }}>
|
|
||||||
<p>Dialog Step 模式: {getStepSettingMode(this, 'exampleFlow', 'dialogStep')}</p>
|
|
||||||
<p>Drawer Step 模式: {getStepSettingMode(this, 'exampleFlow', 'drawerStep')}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册示例流程
|
|
||||||
ExampleModel.registerFlow({
|
|
||||||
key: 'exampleFlow',
|
|
||||||
title: '示例流程',
|
|
||||||
auto: true,
|
|
||||||
steps: {
|
|
||||||
// 使用默认的 dialog 模式
|
|
||||||
dialogStep: {
|
|
||||||
title: '对话框步骤',
|
|
||||||
use: 'exampleAction',
|
|
||||||
// settingMode 默认为 'dialog'
|
|
||||||
uiSchema: {
|
|
||||||
message: {
|
|
||||||
type: 'string',
|
|
||||||
title: '消息内容',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input.TextArea',
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: 'string',
|
|
||||||
title: '消息类型',
|
|
||||||
enum: [
|
|
||||||
{ label: '信息', value: 'info' },
|
|
||||||
{ label: '成功', value: 'success' },
|
|
||||||
{ label: '警告', value: 'warning' },
|
|
||||||
{ label: '错误', value: 'error' },
|
|
||||||
],
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
message: '这是一个对话框配置的步骤',
|
|
||||||
type: 'info',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 使用 drawer 模式
|
|
||||||
drawerStep: {
|
|
||||||
title: '抽屉步骤',
|
|
||||||
use: 'exampleAction',
|
|
||||||
settingMode: 'drawer', // 配置为使用抽屉模式
|
|
||||||
uiSchema: {
|
|
||||||
title: {
|
|
||||||
type: 'string',
|
|
||||||
title: '标题',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: 'string',
|
|
||||||
title: '描述',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input.TextArea',
|
|
||||||
},
|
|
||||||
priority: {
|
|
||||||
type: 'string',
|
|
||||||
title: '优先级',
|
|
||||||
enum: [
|
|
||||||
{ label: '低', value: 'low' },
|
|
||||||
{ label: '中', value: 'medium' },
|
|
||||||
{ label: '高', value: 'high' },
|
|
||||||
],
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Radio.Group',
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: 'boolean',
|
|
||||||
title: '启用',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Switch',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
title: '这是一个抽屉配置的步骤',
|
|
||||||
description: '抽屉模式提供更大的配置空间',
|
|
||||||
priority: 'medium',
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 注册示例动作 (需要通过 FlowEngine 实例注册)
|
|
||||||
// ExampleModel.registerAction({
|
|
||||||
// name: 'exampleAction',
|
|
||||||
// title: '示例动作',
|
|
||||||
// handler: (ctx, params) => {
|
|
||||||
// console.log('执行示例动作:', params);
|
|
||||||
// return params;
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
export { ExampleModel };
|
|
@ -1,121 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 React from 'react';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import { FlowModel } from '../../../../../models';
|
|
||||||
import { openStepSettings } from '../StepSettings';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试修复后的抽屉功能
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 创建一个测试模型
|
|
||||||
class TestDrawerModel extends FlowModel {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div style={{ padding: 20, border: '1px solid #ccc', borderRadius: 4 }}>
|
|
||||||
<h3>测试修复后的抽屉功能</h3>
|
|
||||||
<p>点击下面的按钮测试抽屉配置功能</p>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
openStepSettings({
|
|
||||||
model: this,
|
|
||||||
flowKey: 'testFlow',
|
|
||||||
stepKey: 'drawerStep',
|
|
||||||
})
|
|
||||||
.then((values) => {
|
|
||||||
console.log('保存的配置:', values);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log('用户取消或出错:', error);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
打开抽屉配置 (修复版)
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册测试流程
|
|
||||||
TestDrawerModel.registerFlow({
|
|
||||||
key: 'testFlow',
|
|
||||||
title: '测试流程',
|
|
||||||
steps: {
|
|
||||||
drawerStep: {
|
|
||||||
title: '抽屉步骤',
|
|
||||||
settingMode: 'drawer', // 使用抽屉模式
|
|
||||||
uiSchema: {
|
|
||||||
title: {
|
|
||||||
type: 'string',
|
|
||||||
title: '标题',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
'x-component-props': {
|
|
||||||
placeholder: '请输入标题',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: 'string',
|
|
||||||
title: '描述',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input.TextArea',
|
|
||||||
'x-component-props': {
|
|
||||||
placeholder: '请输入描述',
|
|
||||||
rows: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: 'boolean',
|
|
||||||
title: '启用',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Switch',
|
|
||||||
},
|
|
||||||
priority: {
|
|
||||||
type: 'string',
|
|
||||||
title: '优先级',
|
|
||||||
enum: [
|
|
||||||
{ label: '低', value: 'low' },
|
|
||||||
{ label: '中', value: 'medium' },
|
|
||||||
{ label: '高', value: 'high' },
|
|
||||||
],
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
},
|
|
||||||
tags: {
|
|
||||||
type: 'array',
|
|
||||||
title: '标签',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
'x-component-props': {
|
|
||||||
mode: 'tags',
|
|
||||||
placeholder: '请输入标签',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
title: '默认标题',
|
|
||||||
description: '默认描述',
|
|
||||||
enabled: true,
|
|
||||||
priority: 'medium',
|
|
||||||
tags: ['测试', '抽屉'],
|
|
||||||
},
|
|
||||||
// 简单的处理函数
|
|
||||||
handler: (ctx, params) => {
|
|
||||||
console.log('执行抽屉步骤:', params);
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export { TestDrawerModel };
|
|
@ -9,23 +9,41 @@
|
|||||||
|
|
||||||
import { define, observable } from '@formily/reactive';
|
import { define, observable } from '@formily/reactive';
|
||||||
import { openStepSettingsDialog } from './components/settings/wrappers/contextual/StepSettingsDialog';
|
import { openStepSettingsDialog } from './components/settings/wrappers/contextual/StepSettingsDialog';
|
||||||
import { StepSettingsDialogProps } from './types';
|
import { StepSettingsDialogProps, ToolbarItemConfig } from './types';
|
||||||
|
import { DefaultSettingsIcon } from './components/settings/wrappers/contextual/DefaultSettingsIcon';
|
||||||
|
|
||||||
export class FlowSettings {
|
export class FlowSettings {
|
||||||
public components: Record<string, any> = {};
|
public components: Record<string, any> = {};
|
||||||
public scopes: Record<string, any> = {};
|
public scopes: Record<string, any> = {};
|
||||||
private antdComponentsLoaded = false;
|
private antdComponentsLoaded = false;
|
||||||
public enabled: boolean;
|
public enabled: boolean;
|
||||||
|
public toolbarItems: ToolbarItemConfig[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// 初始默认为 false,由 SchemaComponentProvider 根据实际设计模式状态同步设置
|
// 初始默认为 false,由 SchemaComponentProvider 根据实际设计模式状态同步设置
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
|
|
||||||
|
// 添加默认的配置项目
|
||||||
|
this.addDefaultToolbarItems();
|
||||||
|
|
||||||
define(this, {
|
define(this, {
|
||||||
enabled: observable,
|
enabled: observable,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加默认的工具栏项目
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private addDefaultToolbarItems(): void {
|
||||||
|
// 添加基础的配置菜单项目(原有的菜单功能)
|
||||||
|
this.toolbarItems.push({
|
||||||
|
key: 'settings-menu',
|
||||||
|
component: DefaultSettingsIcon,
|
||||||
|
sort: 0, // 默认为0,作为第一个添加的项目
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载 FlowSettings 所需的资源。
|
* 加载 FlowSettings 所需的资源。
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
@ -193,6 +211,105 @@ export class FlowSettings {
|
|||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加扩展工具栏项目
|
||||||
|
* @param {ToolbarItemConfig} config 项目配置
|
||||||
|
* @example
|
||||||
|
* // 添加一个复制图标组件
|
||||||
|
* const CopyIcon = ({ model }) => {
|
||||||
|
* const handleCopy = () => {
|
||||||
|
* navigator.clipboard.writeText(model.uid);
|
||||||
|
* };
|
||||||
|
* return (
|
||||||
|
* <Tooltip title="复制">
|
||||||
|
* <CopyOutlined onClick={handleCopy} style={{ cursor: 'pointer', fontSize: 12 }} />
|
||||||
|
* </Tooltip>
|
||||||
|
* );
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* flowSettings.addToolbarItem({
|
||||||
|
* key: 'copy',
|
||||||
|
* component: CopyIcon,
|
||||||
|
* sort: 10 // 数字越小越靠右
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // 添加下拉菜单项目组件
|
||||||
|
* const MoreActionsIcon = ({ model }) => {
|
||||||
|
* const menuItems = [
|
||||||
|
* { key: 'action1', label: '操作1', onClick: () => console.log('操作1', model) },
|
||||||
|
* { key: 'action2', label: '操作2', onClick: () => console.log('操作2', model) }
|
||||||
|
* ];
|
||||||
|
* return (
|
||||||
|
* <Dropdown menu={{ items: menuItems }} trigger={['hover']}>
|
||||||
|
* <MoreOutlined style={{ cursor: 'pointer', fontSize: 12 }} />
|
||||||
|
* </Dropdown>
|
||||||
|
* );
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* flowSettings.addToolbarItem({
|
||||||
|
* key: 'more-actions',
|
||||||
|
* component: MoreActionsIcon,
|
||||||
|
* visible: (model) => model.someCondition,
|
||||||
|
* sort: 20 // 数字越大越靠左
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
public addToolbarItem(config: ToolbarItemConfig): void {
|
||||||
|
// 检查是否已存在相同 key 的项目
|
||||||
|
const existingIndex = this.toolbarItems.findIndex((item) => item.key === config.key);
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
console.warn(`FlowSettings: Toolbar item with key '${config.key}' already exists and will be replaced.`);
|
||||||
|
this.toolbarItems[existingIndex] = config;
|
||||||
|
} else {
|
||||||
|
this.toolbarItems.push(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按 sort 字段反向排序,sort 越小越靠右(先添加的在右边)
|
||||||
|
this.toolbarItems.sort((a, b) => (b.sort || 0) - (a.sort || 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加工具栏项目
|
||||||
|
* @param {ToolbarItemConfig[]} configs 项目配置数组
|
||||||
|
* @example
|
||||||
|
* flowSettings.addToolbarItems([
|
||||||
|
* { key: 'copy', component: CopyIcon, sort: 10 },
|
||||||
|
* { key: 'edit', component: EditIcon, sort: 20 }
|
||||||
|
* ]);
|
||||||
|
*/
|
||||||
|
public addToolbarItems(configs: ToolbarItemConfig[]): void {
|
||||||
|
configs.forEach((config) => this.addToolbarItem(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除工具栏项目
|
||||||
|
* @param {string} key 项目的唯一标识
|
||||||
|
* @example
|
||||||
|
* flowSettings.removeToolbarItem('copy');
|
||||||
|
*/
|
||||||
|
public removeToolbarItem(key: string): void {
|
||||||
|
const index = this.toolbarItems.findIndex((item) => item.key === key);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.toolbarItems.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有工具栏项目配置
|
||||||
|
* @returns {ToolbarItemConfig[]} 所有项目配置
|
||||||
|
*/
|
||||||
|
public getToolbarItems(): ToolbarItemConfig[] {
|
||||||
|
return [...this.toolbarItems];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空所有工具栏项目
|
||||||
|
* @example
|
||||||
|
* flowSettings.clearToolbarItems();
|
||||||
|
*/
|
||||||
|
public clearToolbarItems(): void {
|
||||||
|
this.toolbarItems = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示单个步骤的配置界面
|
* 显示单个步骤的配置界面
|
||||||
* @param {StepSettingsDialogProps} props 步骤设置对话框的属性
|
* @param {StepSettingsDialogProps} props 步骤设置对话框的属性
|
||||||
|
@ -361,3 +361,17 @@ export interface FieldFlowModelMeta extends FlowModelMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type { ForkFlowModel } from './models';
|
export type { ForkFlowModel } from './models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具栏项目配置接口
|
||||||
|
*/
|
||||||
|
export interface ToolbarItemConfig {
|
||||||
|
/** 项目的唯一标识 */
|
||||||
|
key: string;
|
||||||
|
/** 项目组件,接收 model 作为 props,内部处理所有逻辑 */
|
||||||
|
component: React.ComponentType<{ model: FlowModel; [key: string]: any }>;
|
||||||
|
/** 是否显示项目的条件函数 */
|
||||||
|
visible?: (model: FlowModel) => boolean;
|
||||||
|
/** 排序权重,数字越小越靠右(先添加的在右边) */
|
||||||
|
sort?: number;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user