三方系统密码加密
This commit is contained in:
parent
adf83458a5
commit
92eb82583f
@ -1,4 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import * as z from 'zod';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@ -8,8 +11,15 @@ import {
|
|||||||
DialogBody,
|
DialogBody,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
@ -26,18 +36,41 @@ interface EditModalProps {
|
|||||||
record?: WorkflowDefinition;
|
record?: WorkflowDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zod 验证 Schema
|
||||||
|
const workflowFormSchema = z.object({
|
||||||
|
name: z.string()
|
||||||
|
.min(1, '请输入流程名称')
|
||||||
|
.max(100, '流程名称不能超过100个字符'),
|
||||||
|
key: z.string()
|
||||||
|
.min(1, '请输入流程标识')
|
||||||
|
.regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, '流程标识只能包含字母、数字、下划线和连字符,且必须以字母或下划线开头')
|
||||||
|
.refine((val) => !/^xml/i.test(val), '流程标识不能以xml开头'),
|
||||||
|
categoryId: z.number({
|
||||||
|
required_error: '请选择流程分类',
|
||||||
|
invalid_type_error: '请选择流程分类',
|
||||||
|
}),
|
||||||
|
formDefinitionId: z.number().optional().nullable(),
|
||||||
|
description: z.string().max(500, '描述不能超过500个字符').optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type WorkflowFormValues = z.infer<typeof workflowFormSchema>;
|
||||||
|
|
||||||
const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, record }) => {
|
const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, record }) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const isEdit = !!record;
|
const isEdit = !!record;
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [categories, setCategories] = useState<WorkflowCategoryResponse[]>([]);
|
const [categories, setCategories] = useState<WorkflowCategoryResponse[]>([]);
|
||||||
const [formDefinitions, setFormDefinitions] = useState<FormDefinitionResponse[]>([]);
|
const [formDefinitions, setFormDefinitions] = useState<FormDefinitionResponse[]>([]);
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
|
const form = useForm<WorkflowFormValues>({
|
||||||
|
resolver: zodResolver(workflowFormSchema),
|
||||||
|
defaultValues: {
|
||||||
name: '',
|
name: '',
|
||||||
key: '',
|
key: '',
|
||||||
categoryId: undefined as number | undefined,
|
categoryId: undefined as any,
|
||||||
formDefinitionId: undefined as number | undefined,
|
formDefinitionId: undefined,
|
||||||
description: '',
|
description: '',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -45,18 +78,18 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
loadCategories();
|
loadCategories();
|
||||||
loadFormDefinitions();
|
loadFormDefinitions();
|
||||||
if (record) {
|
if (record) {
|
||||||
setFormData({
|
form.reset({
|
||||||
name: record.name,
|
name: record.name,
|
||||||
key: record.key,
|
key: record.key,
|
||||||
categoryId: record.categoryId,
|
categoryId: record.categoryId,
|
||||||
formDefinitionId: record.formDefinitionId,
|
formDefinitionId: record.formDefinitionId || undefined,
|
||||||
description: record.description || '',
|
description: record.description || '',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setFormData({
|
form.reset({
|
||||||
name: '',
|
name: '',
|
||||||
key: '',
|
key: '',
|
||||||
categoryId: undefined,
|
categoryId: undefined as any,
|
||||||
formDefinitionId: undefined,
|
formDefinitionId: undefined,
|
||||||
description: '',
|
description: '',
|
||||||
});
|
});
|
||||||
@ -95,80 +128,38 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async (values: WorkflowFormValues) => {
|
||||||
// 验证
|
|
||||||
if (!formData.name.trim()) {
|
|
||||||
toast({
|
|
||||||
title: '验证失败',
|
|
||||||
description: '请输入流程名称',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!formData.key.trim()) {
|
|
||||||
toast({
|
|
||||||
title: '验证失败',
|
|
||||||
description: '请输入流程标识',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(formData.key)) {
|
|
||||||
toast({
|
|
||||||
title: '验证失败',
|
|
||||||
description: '流程标识只能包含字母、数字、下划线和连字符,且必须以字母或下划线开头',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (/^xml/i.test(formData.key)) {
|
|
||||||
toast({
|
|
||||||
title: '验证失败',
|
|
||||||
description: '流程标识不能以xml开头',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!formData.categoryId) {
|
|
||||||
toast({
|
|
||||||
title: '验证失败',
|
|
||||||
description: '请选择流程分类',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
if (isEdit && record) {
|
if (isEdit && record) {
|
||||||
// 更新时需要传递完整的数据和版本号
|
// 更新时需要传递完整的数据和版本号
|
||||||
const submitData: WorkflowDefinitionRequest = {
|
const submitData: WorkflowDefinitionRequest = {
|
||||||
name: formData.name,
|
name: values.name,
|
||||||
key: formData.key,
|
key: values.key,
|
||||||
categoryId: formData.categoryId,
|
categoryId: values.categoryId,
|
||||||
formDefinitionId: formData.formDefinitionId,
|
formDefinitionId: values.formDefinitionId,
|
||||||
flowVersion: record.flowVersion, // 流程版本
|
flowVersion: record.flowVersion,
|
||||||
description: formData.description,
|
description: values.description,
|
||||||
graph: record.graph,
|
graph: record.graph,
|
||||||
bpmnXml: record.bpmnXml,
|
bpmnXml: record.bpmnXml,
|
||||||
status: record.status,
|
status: record.status,
|
||||||
version: record.version, // 乐观锁版本号
|
version: record.version,
|
||||||
};
|
};
|
||||||
|
|
||||||
await updateDefinition(record.id, submitData);
|
await updateDefinition(record.id, submitData);
|
||||||
toast({
|
toast({
|
||||||
title: '更新成功',
|
title: '更新成功',
|
||||||
description: `工作流 "${formData.name}" 已更新`,
|
description: `工作流 "${values.name}" 已更新`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 新建时初始化基本数据
|
// 新建时初始化基本数据
|
||||||
const submitData: WorkflowDefinitionRequest = {
|
const submitData: WorkflowDefinitionRequest = {
|
||||||
name: formData.name,
|
name: values.name,
|
||||||
key: formData.key,
|
key: values.key,
|
||||||
categoryId: formData.categoryId,
|
categoryId: values.categoryId,
|
||||||
formDefinitionId: formData.formDefinitionId,
|
formDefinitionId: values.formDefinitionId,
|
||||||
flowVersion: 1, // 新建时流程版本为1
|
flowVersion: 1,
|
||||||
description: formData.description,
|
description: values.description,
|
||||||
graph: { nodes: [], edges: [] },
|
graph: { nodes: [], edges: [] },
|
||||||
status: 'DRAFT',
|
status: 'DRAFT',
|
||||||
};
|
};
|
||||||
@ -176,7 +167,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
await saveDefinition(submitData);
|
await saveDefinition(submitData);
|
||||||
toast({
|
toast({
|
||||||
title: '创建成功',
|
title: '创建成功',
|
||||||
description: `工作流 "${formData.name}" 已创建`,
|
description: `工作流 "${values.name}" 已创建`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onSuccess?.();
|
onSuccess?.();
|
||||||
@ -200,25 +191,29 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||||
<DialogBody className="grid gap-6">
|
<DialogBody className="grid gap-6">
|
||||||
{/* 流程分类 */}
|
{/* 流程分类 */}
|
||||||
<div className="space-y-2">
|
<FormField
|
||||||
<Label htmlFor="categoryId">
|
control={form.control}
|
||||||
|
name="categoryId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
流程分类 <span className="text-destructive">*</span>
|
流程分类 <span className="text-destructive">*</span>
|
||||||
</Label>
|
</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
value={formData.categoryId?.toString() || undefined}
|
value={field.value?.toString()}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => field.onChange(Number(value))}
|
||||||
setFormData(prev => ({
|
|
||||||
...prev,
|
|
||||||
categoryId: Number(value),
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
disabled={isEdit}
|
disabled={isEdit}
|
||||||
>
|
>
|
||||||
<SelectTrigger id="categoryId" className="h-10">
|
<FormControl>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
<SelectValue placeholder="请选择流程分类" />
|
<SelectValue placeholder="请选择流程分类" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{categories.map(cat => (
|
{categories.map(cat => (
|
||||||
<SelectItem key={cat.id} value={cat.id.toString()}>
|
<SelectItem key={cat.id} value={cat.id.toString()}>
|
||||||
@ -232,41 +227,43 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
编辑时不可修改分类
|
编辑时不可修改分类
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 启动表单 */}
|
{/* 启动表单 */}
|
||||||
<div className="space-y-2">
|
<FormField
|
||||||
<Label htmlFor="formDefinitionId">
|
control={form.control}
|
||||||
启动表单
|
name="formDefinitionId"
|
||||||
</Label>
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>启动表单</FormLabel>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Select
|
<Select
|
||||||
value={formData.formDefinitionId?.toString() || ''}
|
value={field.value?.toString() || ''}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => field.onChange(value ? Number(value) : undefined)}
|
||||||
setFormData(prev => ({
|
|
||||||
...prev,
|
|
||||||
formDefinitionId: value ? Number(value) : undefined,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger id="formDefinitionId" className="h-10 flex-1">
|
<FormControl>
|
||||||
|
<SelectTrigger className="h-10 flex-1">
|
||||||
<SelectValue placeholder="请选择启动表单(可选)" />
|
<SelectValue placeholder="请选择启动表单(可选)" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{formDefinitions.map(form => (
|
{formDefinitions.map(formDef => (
|
||||||
<SelectItem key={form.id} value={form.id.toString()}>
|
<SelectItem key={formDef.id} value={formDef.id.toString()}>
|
||||||
{form.name} ({form.key})
|
{formDef.name} ({formDef.key})
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{formData.formDefinitionId && (
|
{field.value && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-10 w-10 shrink-0"
|
className="h-10 w-10 shrink-0"
|
||||||
onClick={() => setFormData(prev => ({ ...prev, formDefinitionId: undefined }))}
|
onClick={() => field.onChange(undefined)}
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
</Button>
|
</Button>
|
||||||
@ -275,68 +272,91 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
选择启动该工作流时需要填写的表单
|
选择启动该工作流时需要填写的表单
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 流程标识 */}
|
{/* 流程标识 */}
|
||||||
<div className="space-y-2">
|
<FormField
|
||||||
<Label htmlFor="key">
|
control={form.control}
|
||||||
|
name="key"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
流程标识 <span className="text-destructive">*</span>
|
流程标识 <span className="text-destructive">*</span>
|
||||||
{isEdit && <span className="text-xs text-muted-foreground ml-1">(不可修改)</span>}
|
{isEdit && <span className="text-xs text-muted-foreground ml-1">(不可修改)</span>}
|
||||||
</Label>
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
id="key"
|
|
||||||
placeholder="例如:jenkins_build_workflow"
|
placeholder="例如:jenkins_build_workflow"
|
||||||
value={formData.key}
|
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, key: e.target.value }))}
|
|
||||||
disabled={isEdit}
|
disabled={isEdit}
|
||||||
className="h-10 font-mono"
|
className="h-10 font-mono"
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
|
</FormControl>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
只能包含字母、数字、下划线和连字符,建议使用小写字母和下划线
|
只能包含字母、数字、下划线和连字符,建议使用小写字母和下划线
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 流程名称 */}
|
{/* 流程名称 */}
|
||||||
<div className="space-y-2">
|
<FormField
|
||||||
<Label htmlFor="name">
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
流程名称 <span className="text-destructive">*</span>
|
流程名称 <span className="text-destructive">*</span>
|
||||||
{isEdit && <span className="text-xs text-muted-foreground ml-1">(不可修改)</span>}
|
{isEdit && <span className="text-xs text-muted-foreground ml-1">(不可修改)</span>}
|
||||||
</Label>
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
|
||||||
placeholder="例如:Jenkins 构建流程"
|
placeholder="例如:Jenkins 构建流程"
|
||||||
value={formData.name}
|
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
||||||
disabled={isEdit}
|
disabled={isEdit}
|
||||||
className="h-10"
|
className="h-10"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 描述 */}
|
{/* 描述 */}
|
||||||
<div className="space-y-2">
|
<FormField
|
||||||
<Label htmlFor="description">
|
control={form.control}
|
||||||
流程描述
|
name="description"
|
||||||
</Label>
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>流程描述</FormLabel>
|
||||||
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="description"
|
|
||||||
placeholder="简要说明此工作流的用途..."
|
placeholder="简要说明此工作流的用途..."
|
||||||
value={formData.description}
|
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
|
||||||
className="min-h-[100px] resize-none"
|
className="min-h-[100px] resize-none"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
</DialogBody>
|
</DialogBody>
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={handleClose} disabled={submitting}>
|
<Button type="button" variant="outline" onClick={handleClose} disabled={submitting}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSubmit} disabled={submitting}>
|
<Button type="submit" disabled={submitting}>
|
||||||
{submitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
{submitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
||||||
{isEdit ? '更新' : '创建'}
|
{isEdit ? '更新' : '创建'}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -414,19 +414,20 @@ const WorkflowDefinitionList: React.FC = () => {
|
|||||||
<Table minWidth="1000px">
|
<Table minWidth="1000px">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead width="200px">流程名称</TableHead>
|
<TableHead width="180px">流程名称</TableHead>
|
||||||
<TableHead width="150px">流程标识</TableHead>
|
<TableHead width="140px">流程标识</TableHead>
|
||||||
<TableHead width="120px">分类</TableHead>
|
<TableHead width="100px">分类</TableHead>
|
||||||
<TableHead width="80px">版本</TableHead>
|
<TableHead width="140px">启动表单</TableHead>
|
||||||
<TableHead width="120px">状态</TableHead>
|
<TableHead width="70px">版本</TableHead>
|
||||||
<TableHead width="150px">描述</TableHead>
|
<TableHead width="100px">状态</TableHead>
|
||||||
|
<TableHead width="150px">创建时间</TableHead>
|
||||||
<TableHead width="180px" sticky>操作</TableHead>
|
<TableHead width="180px" sticky>操作</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={7} className="h-24 text-center">
|
<TableCell colSpan={8} className="h-24 text-center">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
||||||
<span className="text-sm text-muted-foreground">加载中...</span>
|
<span className="text-sm text-muted-foreground">加载中...</span>
|
||||||
@ -435,29 +436,47 @@ const WorkflowDefinitionList: React.FC = () => {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
) : data?.content && data.content.length > 0 ? (
|
) : data?.content && data.content.length > 0 ? (
|
||||||
data.content.map((record) => {
|
data.content.map((record) => {
|
||||||
const categoryInfo = categories.find(c => c.id === record.categoryId);
|
|
||||||
const isDraft = record.status === 'DRAFT';
|
const isDraft = record.status === 'DRAFT';
|
||||||
|
// 优先使用后端返回的 category 对象,fallback 到前端查找
|
||||||
|
const categoryInfo = record.category || categories.find(c => c.id === record.categoryId);
|
||||||
return (
|
return (
|
||||||
<TableRow key={record.id} className="hover:bg-muted/50">
|
<TableRow key={record.id} className="hover:bg-muted/50">
|
||||||
<TableCell width="200px" className="font-medium">{record.name}</TableCell>
|
<TableCell width="180px" className="font-medium">{record.name}</TableCell>
|
||||||
<TableCell width="150px">
|
<TableCell width="140px">
|
||||||
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
|
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
|
||||||
{record.key}
|
{record.key}
|
||||||
</code>
|
</code>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell width="120px">
|
<TableCell width="100px">
|
||||||
{categoryInfo ? (
|
{categoryInfo ? (
|
||||||
<Badge variant="outline">{categoryInfo.name}</Badge>
|
<Badge variant="outline">{categoryInfo.name}</Badge>
|
||||||
) : (
|
) : (
|
||||||
<Badge variant="outline">未分类</Badge>
|
<Badge variant="outline">未分类</Badge>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell width="80px">
|
<TableCell width="140px">
|
||||||
|
{record.formDefinitionName ? (
|
||||||
|
<Badge variant="secondary" className="font-normal">
|
||||||
|
{record.formDefinitionName}
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">无</span>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell width="70px">
|
||||||
<span className="font-mono text-sm">{record.flowVersion || 1}</span>
|
<span className="font-mono text-sm">{record.flowVersion || 1}</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell width="120px">{getStatusBadge(record.status || 'DRAFT')}</TableCell>
|
<TableCell width="100px">{getStatusBadge(record.status || 'DRAFT')}</TableCell>
|
||||||
<TableCell width="150px">
|
<TableCell width="150px">
|
||||||
<span className="text-sm line-clamp-1">{record.description || '-'}</span>
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{record.createTime ? new Date(record.createTime).toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
}) : '-'}
|
||||||
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell width="180px" sticky>
|
<TableCell width="180px" sticky>
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
@ -526,7 +545,7 @@ const WorkflowDefinitionList: React.FC = () => {
|
|||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={7} className="h-24 text-center">
|
<TableCell colSpan={8} className="h-24 text-center">
|
||||||
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
|
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
|
||||||
<Workflow className="w-16 h-16 mb-4 text-muted-foreground/50" />
|
<Workflow className="w-16 h-16 mb-4 text-muted-foreground/50" />
|
||||||
<div className="text-lg font-semibold mb-2">暂无工作流定义</div>
|
<div className="text-lg font-semibold mb-2">暂无工作流定义</div>
|
||||||
|
|||||||
@ -12,7 +12,9 @@ export interface WorkflowDefinition extends BaseResponse {
|
|||||||
name: string;
|
name: string;
|
||||||
key: string;
|
key: string;
|
||||||
categoryId?: number; // 分类ID
|
categoryId?: number; // 分类ID
|
||||||
|
category?: WorkflowCategoryResponse | null; // 分类对象(后端返回)
|
||||||
formDefinitionId?: number; // 启动表单ID
|
formDefinitionId?: number; // 启动表单ID
|
||||||
|
formDefinitionName?: string | null; // 启动表单名称(后端返回)
|
||||||
processDefinitionId?: string; // 流程定义ID
|
processDefinitionId?: string; // 流程定义ID
|
||||||
flowVersion: number; // 流程版本
|
flowVersion: number; // 流程版本
|
||||||
bpmnXml?: string; // BPMN XML内容
|
bpmnXml?: string; // BPMN XML内容
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user