重构消息通知弹窗
This commit is contained in:
parent
88f5ec1d5c
commit
96517b809e
@ -95,6 +95,7 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
|
||||
// 打开审批对话框
|
||||
const handleOpenApproval = (task: PendingApprovalTask, result: ApprovalResult) => {
|
||||
console.log('打开审批对话框,任务数据:', task); // 调试日志
|
||||
setSelectedTask(task);
|
||||
setApprovalResult(result);
|
||||
setApprovalComment('');
|
||||
@ -180,38 +181,41 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
{approvalList.map((task) => (
|
||||
<div
|
||||
key={task.taskId}
|
||||
className="group relative p-4 border rounded-lg hover:border-primary/50 hover:shadow-md transition-all bg-card"
|
||||
className="group relative p-4 border border-gray-200/80 rounded-xl hover:border-orange-300 hover:shadow-lg transition-all duration-300 bg-gradient-to-br from-white to-orange-50/30 overflow-hidden"
|
||||
>
|
||||
{/* 装饰性渐变背景 */}
|
||||
<div className="absolute top-0 right-0 w-24 h-24 bg-gradient-to-br from-orange-100/40 to-transparent rounded-full blur-2xl -z-0" />
|
||||
|
||||
{/* 左侧内容区 */}
|
||||
<div className="flex gap-4">
|
||||
<div className="flex gap-3 relative z-10">
|
||||
{/* 应用图标 */}
|
||||
<div className="shrink-0">
|
||||
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center">
|
||||
<Package className="h-6 w-6 text-primary" />
|
||||
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-orange-500 to-orange-600 flex items-center justify-center shadow-lg shadow-orange-500/30">
|
||||
<Package className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 主要信息 */}
|
||||
<div className="flex-1 min-w-0 space-y-2">
|
||||
<div className="flex-1 min-w-0 space-y-1.5">
|
||||
{/* 标题行 */}
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="text-base font-bold text-foreground truncate">
|
||||
<h3 className="text-base font-bold text-gray-900 truncate">
|
||||
{task.applicationName}
|
||||
</h3>
|
||||
<span className="shrink-0 text-xs px-1.5 py-0.5 bg-orange-100 text-orange-700 rounded font-medium">
|
||||
<span className="shrink-0 text-xs px-1.5 py-0.5 bg-gradient-to-r from-orange-500 to-orange-600 text-white rounded font-semibold shadow-sm">
|
||||
#{task.deployRecordId}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<code className="px-1.5 py-0.5 bg-muted rounded font-mono">
|
||||
<div className="flex items-center gap-2 text-xs text-gray-600">
|
||||
<code className="px-1.5 py-0.5 bg-gray-100 text-gray-700 rounded font-mono font-medium">
|
||||
{task.applicationCode}
|
||||
</code>
|
||||
<span>•</span>
|
||||
<span>{task.environmentName}</span>
|
||||
<span>•</span>
|
||||
<span>{task.teamName}</span>
|
||||
<span className="text-gray-400">•</span>
|
||||
<span className="font-medium">{task.environmentName}</span>
|
||||
<span className="text-gray-400">•</span>
|
||||
<span className="font-medium">{task.teamName}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -219,7 +223,7 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
<div className="flex gap-2 shrink-0">
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-8 bg-green-600 hover:bg-green-700 text-white shadow-sm"
|
||||
className="h-8 px-3 bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white shadow-md shadow-green-500/30 hover:shadow-lg hover:shadow-green-500/40 transition-all duration-200 font-semibold text-xs"
|
||||
onClick={() => handleOpenApproval(task, ApprovalResult.APPROVED)}
|
||||
>
|
||||
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
||||
@ -228,7 +232,7 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 border-red-200 text-red-600 hover:bg-red-50 hover:text-red-700 hover:border-red-300"
|
||||
className="h-8 px-3 border-2 border-red-200 text-red-600 hover:bg-red-50 hover:text-red-700 hover:border-red-400 font-semibold transition-all duration-200 text-xs"
|
||||
onClick={() => handleOpenApproval(task, ApprovalResult.REJECTED)}
|
||||
>
|
||||
<XCircle className="h-3.5 w-3.5 mr-1" />
|
||||
@ -239,14 +243,15 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
|
||||
{/* 审批内容 */}
|
||||
{(task.approvalTitle || task.approvalContent) && (
|
||||
<div className="p-2.5 bg-orange-50/60 border border-orange-100 rounded text-sm">
|
||||
<div className="p-2.5 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200/60 rounded-lg text-sm shadow-sm">
|
||||
{task.approvalTitle && (
|
||||
<div className="font-medium text-foreground mb-0.5">
|
||||
<div className="font-semibold text-gray-900 mb-0.5 flex items-center gap-1.5 text-xs">
|
||||
<div className="w-1 h-3 bg-orange-500 rounded-full" />
|
||||
{task.approvalTitle}
|
||||
</div>
|
||||
)}
|
||||
{task.approvalContent && (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<div className="text-xs text-gray-600 leading-relaxed pl-2.5">
|
||||
{task.approvalContent}
|
||||
</div>
|
||||
)}
|
||||
@ -255,26 +260,26 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
|
||||
{/* 底部信息栏 */}
|
||||
<div className="flex items-center justify-between pt-1">
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="h-3 w-3" />
|
||||
<span>{task.deployBy}</span>
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
<div className="flex items-center gap-1 text-gray-600">
|
||||
<User className="h-3 w-3 text-blue-600" />
|
||||
<span className="font-medium">{task.deployBy}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="h-3 w-3" />
|
||||
<span>{formatTime(task.deployStartTime)}</span>
|
||||
<div className="flex items-center gap-1 text-gray-600">
|
||||
<Calendar className="h-3 w-3 text-purple-600" />
|
||||
<span className="font-medium">{formatTime(task.deployStartTime)}</span>
|
||||
</div>
|
||||
{task.pendingDuration && (
|
||||
<div className="flex items-center gap-1 text-amber-600">
|
||||
<Clock className="h-3 w-3" />
|
||||
<span>等待 {formatDuration(task.pendingDuration)}</span>
|
||||
<div className="flex items-center gap-1 px-1.5 py-0.5 bg-amber-100 rounded">
|
||||
<Clock className="h-3 w-3 text-amber-600" />
|
||||
<span className="text-amber-700 font-semibold">等待 {formatDuration(task.pendingDuration)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 text-xs opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
className="h-6 px-2 text-xs text-orange-600 hover:text-orange-700 hover:bg-orange-50 opacity-0 group-hover:opacity-100 transition-all duration-200 font-medium"
|
||||
onClick={() => handleViewDetail(task)}
|
||||
>
|
||||
查看详情 →
|
||||
@ -283,9 +288,9 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
|
||||
{/* 备注(如果有) */}
|
||||
{task.deployRemark && (
|
||||
<div className="flex items-start gap-1.5 p-2 bg-muted/30 rounded text-xs">
|
||||
<FileText className="h-3 w-3 text-muted-foreground shrink-0 mt-0.5" />
|
||||
<span className="text-muted-foreground">{task.deployRemark}</span>
|
||||
<div className="flex items-start gap-1.5 p-2 bg-gray-50 border border-gray-200 rounded text-xs">
|
||||
<FileText className="h-3 w-3 text-gray-600 shrink-0 mt-0.5" />
|
||||
<span className="text-gray-700 leading-relaxed">{task.deployRemark}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -300,77 +305,107 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
||||
|
||||
{/* 审批确认对话框 */}
|
||||
<AlertDialog open={approvalDialogOpen} onOpenChange={setApprovalDialogOpen}>
|
||||
<AlertDialogContent className="max-w-md">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="flex items-center gap-2">
|
||||
<AlertDialogContent className="max-w-lg">
|
||||
<AlertDialogHeader className="space-y-3">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className={
|
||||
approvalResult === ApprovalResult.APPROVED
|
||||
? "w-16 h-16 rounded-full bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center shadow-lg shadow-green-500/30"
|
||||
: "w-16 h-16 rounded-full bg-gradient-to-br from-red-500 to-red-600 flex items-center justify-center shadow-lg shadow-red-500/30"
|
||||
}>
|
||||
{approvalResult === ApprovalResult.APPROVED ? (
|
||||
<>
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
确认通过审批
|
||||
</>
|
||||
<CheckCircle className="h-8 w-8 text-white" />
|
||||
) : (
|
||||
<>
|
||||
<XCircle className="h-5 w-5 text-red-600" />
|
||||
确认拒绝审批
|
||||
</>
|
||||
<XCircle className="h-8 w-8 text-white" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<AlertDialogTitle className="text-center text-xl font-bold">
|
||||
{approvalResult === ApprovalResult.APPROVED ? '确认通过审批' : '确认拒绝审批'}
|
||||
</AlertDialogTitle>
|
||||
</AlertDialogHeader>
|
||||
<div className="space-y-3 px-6 py-4">
|
||||
<div className="space-y-4 px-6 py-2">
|
||||
{selectedTask && (
|
||||
<>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
您即将{approvalResult === ApprovalResult.APPROVED ? '通过' : '拒绝'}以下部署申请:
|
||||
<div className="text-center text-sm text-gray-600 px-4">
|
||||
您即将
|
||||
<span className={
|
||||
approvalResult === ApprovalResult.APPROVED
|
||||
? "font-semibold text-green-600 mx-1"
|
||||
: "font-semibold text-red-600 mx-1"
|
||||
}>
|
||||
{approvalResult === ApprovalResult.APPROVED ? '通过' : '拒绝'}
|
||||
</span>
|
||||
以下部署申请
|
||||
</div>
|
||||
<div className="p-3 bg-muted/50 rounded-md space-y-1.5 text-xs">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground min-w-[56px]">记录ID</span>
|
||||
<span className="font-mono font-semibold">#{selectedTask.deployRecordId}</span>
|
||||
<div className="p-4 bg-gradient-to-br from-gray-50 to-gray-100/50 border border-gray-200 rounded-xl space-y-3 text-sm shadow-sm">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-gray-500 min-w-[64px] font-medium">记录ID</span>
|
||||
<span className="font-mono font-bold text-orange-600">#{selectedTask.deployRecordId}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground min-w-[56px]">应用</span>
|
||||
<span className="font-medium">{selectedTask.applicationName}</span>
|
||||
<div className="h-px bg-gray-200" />
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-gray-500 min-w-[64px] font-medium">应用</span>
|
||||
<span className="font-semibold text-gray-900">{selectedTask.applicationName}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground min-w-[56px]">环境</span>
|
||||
<span className="font-medium">{selectedTask.environmentName}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-gray-500 min-w-[64px] font-medium">环境</span>
|
||||
<span className="font-semibold text-gray-900">{selectedTask.environmentName}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-gray-500 min-w-[64px] font-medium">申请人</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground min-w-[56px]">申请人</span>
|
||||
<span className="font-medium">{selectedTask.deployBy}</span>
|
||||
<div className="w-6 h-6 rounded-full bg-blue-100 flex items-center justify-center">
|
||||
<User className="h-3.5 w-3.5 text-blue-600" />
|
||||
</div>
|
||||
<span className="font-semibold text-gray-900">{selectedTask.deployBy}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-foreground flex items-center gap-1">
|
||||
<div className="space-y-2.5">
|
||||
<label className="text-sm font-semibold text-gray-900 flex items-center gap-2">
|
||||
<div className="w-1 h-4 bg-orange-500 rounded-full" />
|
||||
审批意见
|
||||
{selectedTask.requireComment && (
|
||||
<span className="text-destructive text-xs">*必填</span>
|
||||
<span className="text-xs px-2 py-0.5 bg-red-100 text-red-700 rounded-md font-medium">必填</span>
|
||||
)}
|
||||
</label>
|
||||
<Textarea
|
||||
placeholder={selectedTask.requireComment ? "请填写审批意见(必填)" : "请填写审批意见(可选)"}
|
||||
placeholder={selectedTask.requireComment ? "请填写审批意见..." : "请填写审批意见(可选)..."}
|
||||
value={approvalComment}
|
||||
onChange={(e) => setApprovalComment(e.target.value)}
|
||||
rows={3}
|
||||
className="resize-none"
|
||||
rows={4}
|
||||
className="resize-none border-gray-300 focus:border-orange-400 focus:ring-orange-400/20 rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogFooter className="gap-3 sm:gap-3">
|
||||
<AlertDialogCancel
|
||||
onClick={() => setApprovalDialogOpen(false)}
|
||||
className="flex-1 h-11 border-2 border-gray-300 hover:bg-gray-50 font-semibold"
|
||||
>
|
||||
取消
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleSubmitApproval}
|
||||
disabled={submitting}
|
||||
className={
|
||||
approvalResult === ApprovalResult.APPROVED
|
||||
? "bg-green-600 hover:bg-green-700"
|
||||
: "bg-red-600 hover:bg-red-700"
|
||||
? "flex-1 h-11 bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white shadow-lg shadow-green-500/30 font-semibold"
|
||||
: "flex-1 h-11 bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white shadow-lg shadow-red-500/30 font-semibold"
|
||||
}
|
||||
>
|
||||
{submitting ? '提交中...' : '确认'}
|
||||
{submitting ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
提交中...
|
||||
</span>
|
||||
) : (
|
||||
'确认'
|
||||
)}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
import request from '@/utils/request';
|
||||
import type { DeployEnvironmentsResponse, StartDeploymentResponse, PendingApprovalTask, CompleteApprovalRequest } from './types';
|
||||
import type {
|
||||
DeployEnvironmentsResponse,
|
||||
StartDeploymentResponse,
|
||||
PendingApprovalTask,
|
||||
PendingApprovalTaskResponse,
|
||||
CompleteApprovalRequest
|
||||
} from './types';
|
||||
|
||||
const DEPLOY_URL = '/api/v1/deploy';
|
||||
|
||||
@ -23,17 +29,61 @@ export const startDeployment = (deployData: Record<string, any>) =>
|
||||
export const getDeployRecordFlowGraph = (deployRecordId: number) =>
|
||||
request.get<import('./types').DeployRecordFlowGraph>(`${DEPLOY_URL}/records/${deployRecordId}/flow-graph`);
|
||||
|
||||
/**
|
||||
* 将后端返回的嵌套结构转换为前端扁平结构
|
||||
*/
|
||||
const transformApprovalTask = (response: PendingApprovalTaskResponse): PendingApprovalTask => {
|
||||
return {
|
||||
// 审批任务基本信息
|
||||
taskId: response.approvalTask.taskId,
|
||||
taskName: response.approvalTask.taskName,
|
||||
taskDescription: response.approvalTask.taskDescription,
|
||||
processInstanceId: response.approvalTask.processInstanceId,
|
||||
processDefinitionId: response.approvalTask.processDefinitionId,
|
||||
assignee: response.approvalTask.assignee || '',
|
||||
createTime: response.approvalTask.createTime,
|
||||
dueDate: response.approvalTask.dueDate || undefined,
|
||||
approvalTitle: response.approvalTask.approvalTitle,
|
||||
approvalContent: response.approvalTask.approvalContent,
|
||||
approvalMode: response.approvalTask.approvalMode,
|
||||
allowDelegate: response.approvalTask.allowDelegate,
|
||||
allowAddSign: response.approvalTask.allowAddSign,
|
||||
requireComment: response.approvalTask.requireComment,
|
||||
pendingDuration: response.approvalTask.pendingDuration,
|
||||
|
||||
// 部署业务上下文信息
|
||||
deployRecordId: response.deployRecord.id,
|
||||
businessKey: response.deployRecord.businessKey,
|
||||
deployRemark: response.deployRecord.remark,
|
||||
deployStartTime: response.deployRecord.startTime,
|
||||
|
||||
teamId: response.team.id,
|
||||
teamName: response.team.name,
|
||||
|
||||
applicationId: response.application.id,
|
||||
applicationCode: response.application.code,
|
||||
applicationName: response.application.name,
|
||||
|
||||
environmentId: response.environment.id,
|
||||
environmentCode: response.environment.code,
|
||||
environmentName: response.environment.name,
|
||||
|
||||
// 部署人信息 - 优先使用 nickname,其次 username
|
||||
deployBy: response.deployUser.nickname || response.deployUser.username,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取我的待审批任务列表
|
||||
* @param workflowDefinitionKeys 工作流定义键列表(可选)
|
||||
* @param teamId 团队ID,用于筛选指定团队的审批任务(可选)
|
||||
* @param environmentId 环境ID,用于筛选指定环境的审批任务(可选)
|
||||
*/
|
||||
export const getMyApprovalTasks = (
|
||||
export const getMyApprovalTasks = async (
|
||||
workflowDefinitionKeys?: string[],
|
||||
teamId?: number,
|
||||
environmentId?: number
|
||||
) => {
|
||||
): Promise<PendingApprovalTask[]> => {
|
||||
const params: Record<string, any> = {};
|
||||
|
||||
if (workflowDefinitionKeys && workflowDefinitionKeys.length > 0) {
|
||||
@ -48,7 +98,13 @@ export const getMyApprovalTasks = (
|
||||
params.environmentId = environmentId;
|
||||
}
|
||||
|
||||
return request.get<PendingApprovalTask[]>(`${DEPLOY_URL}/my-approval-tasks`, { params });
|
||||
const response = await request.get<PendingApprovalTaskResponse[]>(
|
||||
`${DEPLOY_URL}/my-approval-tasks`,
|
||||
{ params }
|
||||
);
|
||||
|
||||
// 将嵌套结构转换为扁平结构
|
||||
return response.map(transformApprovalTask);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -120,7 +120,57 @@ export interface StartDeploymentResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* 待审批任务
|
||||
* 后端返回的待审批任务原始结构(嵌套对象)
|
||||
*/
|
||||
export interface PendingApprovalTaskResponse {
|
||||
approvalTask: {
|
||||
taskId: string;
|
||||
taskName: string;
|
||||
taskDescription?: string;
|
||||
processInstanceId: string;
|
||||
processDefinitionId: string;
|
||||
assignee: string | null;
|
||||
createTime: string;
|
||||
dueDate?: string | null;
|
||||
pendingDuration?: number;
|
||||
approvalTitle?: string;
|
||||
approvalContent?: string;
|
||||
approvalMode: 'SINGLE' | 'MULTI' | 'OR' | 'ANY';
|
||||
allowDelegate?: boolean;
|
||||
allowAddSign?: boolean;
|
||||
requireComment?: boolean;
|
||||
};
|
||||
deployRecord: {
|
||||
id: number;
|
||||
businessKey: string;
|
||||
remark?: string;
|
||||
startTime: string;
|
||||
};
|
||||
team: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
application: {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
};
|
||||
environment: {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
};
|
||||
deployUser: {
|
||||
username: string;
|
||||
nickname: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
departmentName: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 待审批任务(前端使用的扁平结构)
|
||||
*/
|
||||
export interface PendingApprovalTask {
|
||||
// ============ 审批任务基本信息 ============
|
||||
@ -134,7 +184,7 @@ export interface PendingApprovalTask {
|
||||
dueDate?: string;
|
||||
approvalTitle?: string;
|
||||
approvalContent?: string;
|
||||
approvalMode: 'SINGLE' | 'MULTI' | 'OR';
|
||||
approvalMode: 'SINGLE' | 'MULTI' | 'OR' | 'ANY';
|
||||
allowDelegate?: boolean;
|
||||
allowAddSign?: boolean;
|
||||
requireComment?: boolean;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { SmartStepEdge as BaseSmartEdge } from '@tisoap/react-flow-smart-edge';
|
||||
import type { FlowNode } from '../types';
|
||||
@ -7,9 +7,15 @@ import { convertToDisplayName } from '@/utils/workflow/variableConversion';
|
||||
/**
|
||||
* 自定义边组件 - 智能避障
|
||||
* 使用 SmartStepEdge 实现智能路由避障,采用直角路径
|
||||
*
|
||||
* 便捷功能:
|
||||
* - 选中高亮显示
|
||||
* - 悬停状态反馈
|
||||
* - 双击重置为默认路径
|
||||
*/
|
||||
const CustomEdge: React.FC<any> = (props) => {
|
||||
const {
|
||||
id,
|
||||
style = {},
|
||||
markerEnd,
|
||||
label,
|
||||
@ -18,7 +24,7 @@ const CustomEdge: React.FC<any> = (props) => {
|
||||
} = props;
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const { getNodes } = useReactFlow();
|
||||
const { getNodes, setEdges } = useReactFlow();
|
||||
|
||||
// 将 label 中的 UUID 转换为节点名
|
||||
const displayLabel = useMemo(() => {
|
||||
@ -61,10 +67,26 @@ const CustomEdge: React.FC<any> = (props) => {
|
||||
const labelBgPadding = [4, 8] as [number, number];
|
||||
const labelBgBorderRadius = 6;
|
||||
|
||||
/**
|
||||
* 双击边重置为默认路径
|
||||
* 清除所有自定义调整,恢复到库的默认智能路由
|
||||
*/
|
||||
const handleDoubleClick = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setEdges(eds => eds.map(ed => {
|
||||
if (ed.id !== id) return ed;
|
||||
// 清除可能存储的自定义路径数据
|
||||
const { vertices, ...restData } = (ed.data || {}) as any;
|
||||
return { ...ed, data: restData };
|
||||
}));
|
||||
}, [id, setEdges]);
|
||||
|
||||
return (
|
||||
<g
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
style={{ cursor: selected ? 'pointer' : 'default' }}
|
||||
>
|
||||
<BaseSmartEdge
|
||||
{...restProps}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user