重构消息通知弹窗
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) => {
|
const handleOpenApproval = (task: PendingApprovalTask, result: ApprovalResult) => {
|
||||||
|
console.log('打开审批对话框,任务数据:', task); // 调试日志
|
||||||
setSelectedTask(task);
|
setSelectedTask(task);
|
||||||
setApprovalResult(result);
|
setApprovalResult(result);
|
||||||
setApprovalComment('');
|
setApprovalComment('');
|
||||||
@ -180,38 +181,41 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
{approvalList.map((task) => (
|
{approvalList.map((task) => (
|
||||||
<div
|
<div
|
||||||
key={task.taskId}
|
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="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">
|
<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-primary" />
|
<Package className="h-6 w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
</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-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<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}
|
{task.applicationName}
|
||||||
</h3>
|
</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}
|
#{task.deployRecordId}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
<div className="flex items-center gap-2 text-xs text-gray-600">
|
||||||
<code className="px-1.5 py-0.5 bg-muted rounded font-mono">
|
<code className="px-1.5 py-0.5 bg-gray-100 text-gray-700 rounded font-mono font-medium">
|
||||||
{task.applicationCode}
|
{task.applicationCode}
|
||||||
</code>
|
</code>
|
||||||
<span>•</span>
|
<span className="text-gray-400">•</span>
|
||||||
<span>{task.environmentName}</span>
|
<span className="font-medium">{task.environmentName}</span>
|
||||||
<span>•</span>
|
<span className="text-gray-400">•</span>
|
||||||
<span>{task.teamName}</span>
|
<span className="font-medium">{task.teamName}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -219,7 +223,7 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
<div className="flex gap-2 shrink-0">
|
<div className="flex gap-2 shrink-0">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
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)}
|
onClick={() => handleOpenApproval(task, ApprovalResult.APPROVED)}
|
||||||
>
|
>
|
||||||
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
||||||
@ -228,7 +232,7 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
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)}
|
onClick={() => handleOpenApproval(task, ApprovalResult.REJECTED)}
|
||||||
>
|
>
|
||||||
<XCircle className="h-3.5 w-3.5 mr-1" />
|
<XCircle className="h-3.5 w-3.5 mr-1" />
|
||||||
@ -239,14 +243,15 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
|
|
||||||
{/* 审批内容 */}
|
{/* 审批内容 */}
|
||||||
{(task.approvalTitle || task.approvalContent) && (
|
{(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 && (
|
{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}
|
{task.approvalTitle}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{task.approvalContent && (
|
{task.approvalContent && (
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-gray-600 leading-relaxed pl-2.5">
|
||||||
{task.approvalContent}
|
{task.approvalContent}
|
||||||
</div>
|
</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 justify-between pt-1">
|
||||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
<div className="flex items-center gap-3 text-xs">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1 text-gray-600">
|
||||||
<User className="h-3 w-3" />
|
<User className="h-3 w-3 text-blue-600" />
|
||||||
<span>{task.deployBy}</span>
|
<span className="font-medium">{task.deployBy}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1 text-gray-600">
|
||||||
<Calendar className="h-3 w-3" />
|
<Calendar className="h-3 w-3 text-purple-600" />
|
||||||
<span>{formatTime(task.deployStartTime)}</span>
|
<span className="font-medium">{formatTime(task.deployStartTime)}</span>
|
||||||
</div>
|
</div>
|
||||||
{task.pendingDuration && (
|
{task.pendingDuration && (
|
||||||
<div className="flex items-center gap-1 text-amber-600">
|
<div className="flex items-center gap-1 px-1.5 py-0.5 bg-amber-100 rounded">
|
||||||
<Clock className="h-3 w-3" />
|
<Clock className="h-3 w-3 text-amber-600" />
|
||||||
<span>等待 {formatDuration(task.pendingDuration)}</span>
|
<span className="text-amber-700 font-semibold">等待 {formatDuration(task.pendingDuration)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
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)}
|
onClick={() => handleViewDetail(task)}
|
||||||
>
|
>
|
||||||
查看详情 →
|
查看详情 →
|
||||||
@ -283,9 +288,9 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
|
|
||||||
{/* 备注(如果有) */}
|
{/* 备注(如果有) */}
|
||||||
{task.deployRemark && (
|
{task.deployRemark && (
|
||||||
<div className="flex items-start gap-1.5 p-2 bg-muted/30 rounded text-xs">
|
<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-muted-foreground shrink-0 mt-0.5" />
|
<FileText className="h-3 w-3 text-gray-600 shrink-0 mt-0.5" />
|
||||||
<span className="text-muted-foreground">{task.deployRemark}</span>
|
<span className="text-gray-700 leading-relaxed">{task.deployRemark}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -300,77 +305,107 @@ export const PendingApprovalModal: React.FC<PendingApprovalModalProps> = ({
|
|||||||
|
|
||||||
{/* 审批确认对话框 */}
|
{/* 审批确认对话框 */}
|
||||||
<AlertDialog open={approvalDialogOpen} onOpenChange={setApprovalDialogOpen}>
|
<AlertDialog open={approvalDialogOpen} onOpenChange={setApprovalDialogOpen}>
|
||||||
<AlertDialogContent className="max-w-md">
|
<AlertDialogContent className="max-w-lg">
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader className="space-y-3">
|
||||||
<AlertDialogTitle className="flex items-center gap-2">
|
<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 ? (
|
{approvalResult === ApprovalResult.APPROVED ? (
|
||||||
<>
|
<CheckCircle className="h-8 w-8 text-white" />
|
||||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
|
||||||
确认通过审批
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<XCircle className="h-8 w-8 text-white" />
|
||||||
<XCircle className="h-5 w-5 text-red-600" />
|
|
||||||
确认拒绝审批
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AlertDialogTitle className="text-center text-xl font-bold">
|
||||||
|
{approvalResult === ApprovalResult.APPROVED ? '确认通过审批' : '确认拒绝审批'}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<div className="space-y-3 px-6 py-4">
|
<div className="space-y-4 px-6 py-2">
|
||||||
{selectedTask && (
|
{selectedTask && (
|
||||||
<>
|
<>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-center text-sm text-gray-600 px-4">
|
||||||
您即将{approvalResult === ApprovalResult.APPROVED ? '通过' : '拒绝'}以下部署申请:
|
您即将
|
||||||
|
<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>
|
||||||
<div className="p-3 bg-muted/50 rounded-md space-y-1.5 text-xs">
|
<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-2">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-muted-foreground min-w-[56px]">记录ID</span>
|
<span className="text-gray-500 min-w-[64px] font-medium">记录ID</span>
|
||||||
<span className="font-mono font-semibold">#{selectedTask.deployRecordId}</span>
|
<span className="font-mono font-bold text-orange-600">#{selectedTask.deployRecordId}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="h-px bg-gray-200" />
|
||||||
<span className="text-muted-foreground min-w-[56px]">应用</span>
|
<div className="flex items-center gap-3">
|
||||||
<span className="font-medium">{selectedTask.applicationName}</span>
|
<span className="text-gray-500 min-w-[64px] font-medium">应用</span>
|
||||||
|
<span className="font-semibold text-gray-900">{selectedTask.applicationName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-muted-foreground min-w-[56px]">环境</span>
|
<span className="text-gray-500 min-w-[64px] font-medium">环境</span>
|
||||||
<span className="font-medium">{selectedTask.environmentName}</span>
|
<span className="font-semibold text-gray-900">{selectedTask.environmentName}</span>
|
||||||
</div>
|
</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">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-muted-foreground min-w-[56px]">申请人</span>
|
<div className="w-6 h-6 rounded-full bg-blue-100 flex items-center justify-center">
|
||||||
<span className="font-medium">{selectedTask.deployBy}</span>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2.5">
|
||||||
<label className="text-sm font-medium text-foreground flex items-center gap-1">
|
<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 && (
|
{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>
|
</label>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder={selectedTask.requireComment ? "请填写审批意见(必填)" : "请填写审批意见(可选)"}
|
placeholder={selectedTask.requireComment ? "请填写审批意见..." : "请填写审批意见(可选)..."}
|
||||||
value={approvalComment}
|
value={approvalComment}
|
||||||
onChange={(e) => setApprovalComment(e.target.value)}
|
onChange={(e) => setApprovalComment(e.target.value)}
|
||||||
rows={3}
|
rows={4}
|
||||||
className="resize-none"
|
className="resize-none border-gray-300 focus:border-orange-400 focus:ring-orange-400/20 rounded-lg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter className="gap-3 sm:gap-3">
|
||||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
<AlertDialogCancel
|
||||||
|
onClick={() => setApprovalDialogOpen(false)}
|
||||||
|
className="flex-1 h-11 border-2 border-gray-300 hover:bg-gray-50 font-semibold"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={handleSubmitApproval}
|
onClick={handleSubmitApproval}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className={
|
className={
|
||||||
approvalResult === ApprovalResult.APPROVED
|
approvalResult === ApprovalResult.APPROVED
|
||||||
? "bg-green-600 hover:bg-green-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"
|
||||||
: "bg-red-600 hover:bg-red-700"
|
: "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>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
import request from '@/utils/request';
|
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';
|
const DEPLOY_URL = '/api/v1/deploy';
|
||||||
|
|
||||||
@ -23,17 +29,61 @@ export const startDeployment = (deployData: Record<string, any>) =>
|
|||||||
export const getDeployRecordFlowGraph = (deployRecordId: number) =>
|
export const getDeployRecordFlowGraph = (deployRecordId: number) =>
|
||||||
request.get<import('./types').DeployRecordFlowGraph>(`${DEPLOY_URL}/records/${deployRecordId}/flow-graph`);
|
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 workflowDefinitionKeys 工作流定义键列表(可选)
|
||||||
* @param teamId 团队ID,用于筛选指定团队的审批任务(可选)
|
* @param teamId 团队ID,用于筛选指定团队的审批任务(可选)
|
||||||
* @param environmentId 环境ID,用于筛选指定环境的审批任务(可选)
|
* @param environmentId 环境ID,用于筛选指定环境的审批任务(可选)
|
||||||
*/
|
*/
|
||||||
export const getMyApprovalTasks = (
|
export const getMyApprovalTasks = async (
|
||||||
workflowDefinitionKeys?: string[],
|
workflowDefinitionKeys?: string[],
|
||||||
teamId?: number,
|
teamId?: number,
|
||||||
environmentId?: number
|
environmentId?: number
|
||||||
) => {
|
): Promise<PendingApprovalTask[]> => {
|
||||||
const params: Record<string, any> = {};
|
const params: Record<string, any> = {};
|
||||||
|
|
||||||
if (workflowDefinitionKeys && workflowDefinitionKeys.length > 0) {
|
if (workflowDefinitionKeys && workflowDefinitionKeys.length > 0) {
|
||||||
@ -48,7 +98,13 @@ export const getMyApprovalTasks = (
|
|||||||
params.environmentId = environmentId;
|
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 {
|
export interface PendingApprovalTask {
|
||||||
// ============ 审批任务基本信息 ============
|
// ============ 审批任务基本信息 ============
|
||||||
@ -134,7 +184,7 @@ export interface PendingApprovalTask {
|
|||||||
dueDate?: string;
|
dueDate?: string;
|
||||||
approvalTitle?: string;
|
approvalTitle?: string;
|
||||||
approvalContent?: string;
|
approvalContent?: string;
|
||||||
approvalMode: 'SINGLE' | 'MULTI' | 'OR';
|
approvalMode: 'SINGLE' | 'MULTI' | 'OR' | 'ANY';
|
||||||
allowDelegate?: boolean;
|
allowDelegate?: boolean;
|
||||||
allowAddSign?: boolean;
|
allowAddSign?: boolean;
|
||||||
requireComment?: 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 { useReactFlow } from '@xyflow/react';
|
||||||
import { SmartStepEdge as BaseSmartEdge } from '@tisoap/react-flow-smart-edge';
|
import { SmartStepEdge as BaseSmartEdge } from '@tisoap/react-flow-smart-edge';
|
||||||
import type { FlowNode } from '../types';
|
import type { FlowNode } from '../types';
|
||||||
@ -7,9 +7,15 @@ import { convertToDisplayName } from '@/utils/workflow/variableConversion';
|
|||||||
/**
|
/**
|
||||||
* 自定义边组件 - 智能避障
|
* 自定义边组件 - 智能避障
|
||||||
* 使用 SmartStepEdge 实现智能路由避障,采用直角路径
|
* 使用 SmartStepEdge 实现智能路由避障,采用直角路径
|
||||||
|
*
|
||||||
|
* 便捷功能:
|
||||||
|
* - 选中高亮显示
|
||||||
|
* - 悬停状态反馈
|
||||||
|
* - 双击重置为默认路径
|
||||||
*/
|
*/
|
||||||
const CustomEdge: React.FC<any> = (props) => {
|
const CustomEdge: React.FC<any> = (props) => {
|
||||||
const {
|
const {
|
||||||
|
id,
|
||||||
style = {},
|
style = {},
|
||||||
markerEnd,
|
markerEnd,
|
||||||
label,
|
label,
|
||||||
@ -18,7 +24,7 @@ const CustomEdge: React.FC<any> = (props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const { getNodes } = useReactFlow();
|
const { getNodes, setEdges } = useReactFlow();
|
||||||
|
|
||||||
// 将 label 中的 UUID 转换为节点名
|
// 将 label 中的 UUID 转换为节点名
|
||||||
const displayLabel = useMemo(() => {
|
const displayLabel = useMemo(() => {
|
||||||
@ -61,10 +67,26 @@ const CustomEdge: React.FC<any> = (props) => {
|
|||||||
const labelBgPadding = [4, 8] as [number, number];
|
const labelBgPadding = [4, 8] as [number, number];
|
||||||
const labelBgBorderRadius = 6;
|
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 (
|
return (
|
||||||
<g
|
<g
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
onDoubleClick={handleDoubleClick}
|
||||||
|
style={{ cursor: selected ? 'pointer' : 'default' }}
|
||||||
>
|
>
|
||||||
<BaseSmartEdge
|
<BaseSmartEdge
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user