增加审批组件
This commit is contained in:
parent
4599367d33
commit
0a64cfc339
@ -38,7 +38,7 @@ const FormDataDetail: React.FC = () => {
|
||||
|
||||
// 返回列表
|
||||
const handleBack = () => {
|
||||
navigate('/form/data');
|
||||
navigate('/workflow/form/data');
|
||||
};
|
||||
|
||||
// 状态徽章
|
||||
|
||||
@ -97,7 +97,7 @@ const FormDataList: React.FC = () => {
|
||||
|
||||
// 查看详情
|
||||
const handleView = (record: FormDataResponse) => {
|
||||
navigate(`/form/data/${record.id}`);
|
||||
navigate(`/workflow/form/data/${record.id}`);
|
||||
};
|
||||
|
||||
// 删除
|
||||
|
||||
@ -76,7 +76,7 @@ const FormDesignerPage: React.FC = () => {
|
||||
|
||||
// 返回列表
|
||||
const handleBack = () => {
|
||||
navigate('/form/definitions');
|
||||
navigate('/workflow/form');
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,248 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Loader2, FileText, Tag, Folder, AlignLeft, Copy } from 'lucide-react';
|
||||
import { getEnabledCategories } from '../../Category/service';
|
||||
import { updateDefinition } from '../service';
|
||||
import type { FormCategoryResponse } from '../../Category/types';
|
||||
import type { FormDefinitionRequest, FormDefinitionResponse } from '../types';
|
||||
|
||||
interface EditBasicInfoModalProps {
|
||||
visible: boolean;
|
||||
record: FormDefinitionResponse | null;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
const EditBasicInfoModal: React.FC<EditBasicInfoModalProps> = ({ visible, record, onClose, onSuccess }) => {
|
||||
const { toast } = useToast();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [categories, setCategories] = useState<FormCategoryResponse[]>([]);
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
key: '',
|
||||
categoryId: undefined as number | undefined,
|
||||
description: '',
|
||||
isTemplate: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadCategories = async () => {
|
||||
try {
|
||||
const result = await getEnabledCategories();
|
||||
setCategories(result || []);
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error);
|
||||
}
|
||||
};
|
||||
if (visible) {
|
||||
loadCategories();
|
||||
if (record) {
|
||||
setFormData({
|
||||
name: record.name,
|
||||
key: record.key,
|
||||
categoryId: record.categoryId,
|
||||
description: record.description || '',
|
||||
isTemplate: record.isTemplate || false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [visible, record]);
|
||||
|
||||
const handleClose = () => {
|
||||
if (!submitting) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!record) return;
|
||||
|
||||
if (!formData.name.trim()) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "验证失败",
|
||||
description: "请输入表单名称",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!formData.key.trim()) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "验证失败",
|
||||
description: "请输入表单标识",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!/^[a-zA-Z0-9-]+$/.test(formData.key)) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "验证失败",
|
||||
description: "表单标识只能包含英文字母、数字和中划线",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const request: FormDefinitionRequest = {
|
||||
name: formData.name,
|
||||
key: formData.key,
|
||||
categoryId: formData.categoryId,
|
||||
description: formData.description,
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
isTemplate: formData.isTemplate,
|
||||
version: record.version, // 乐观锁版本号
|
||||
};
|
||||
|
||||
await updateDefinition(record.id, request);
|
||||
toast({
|
||||
title: "更新成功",
|
||||
description: `表单 "${formData.name}" 的基本信息已更新`,
|
||||
});
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('更新表单失败:', error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "更新失败",
|
||||
description: error instanceof Error ? error.message : '未知错误,请稍后重试',
|
||||
});
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>编辑基本信息</DialogTitle>
|
||||
<DialogDescription>
|
||||
修改表单的名称、标识、分类等基本信息
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-6 py-4">
|
||||
{/* 第一行:表单名称 + 表单标识 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-name" className="flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||
表单名称 <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
placeholder="例如:员工请假申请表"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
||||
className="h-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-key" className="flex items-center gap-2">
|
||||
<Tag className="h-4 w-4 text-muted-foreground" />
|
||||
表单标识 <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="edit-key"
|
||||
placeholder="例如:employee-leave-form"
|
||||
value={formData.key}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, key: e.target.value }))}
|
||||
className="h-10 font-mono"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
英文字母、数字和中划线
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 第二行:分类 + 设为模板 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-category" className="flex items-center gap-2">
|
||||
<Folder className="h-4 w-4 text-muted-foreground" />
|
||||
表单分类
|
||||
</Label>
|
||||
<Select
|
||||
value={formData.categoryId?.toString() || undefined}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, categoryId: Number(value) }))}
|
||||
>
|
||||
<SelectTrigger id="edit-category" className="h-10">
|
||||
<SelectValue placeholder="选择表单所属分类" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map(cat => (
|
||||
<SelectItem key={cat.id} value={cat.id.toString()}>
|
||||
{cat.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-isTemplate" className="flex items-center gap-2">
|
||||
<Copy className="h-4 w-4 text-muted-foreground" />
|
||||
设为表单模板
|
||||
</Label>
|
||||
<div className="flex items-center h-10 rounded-lg border px-4 bg-muted/30">
|
||||
<div className="flex-1">
|
||||
<span className="text-sm">启用模板功能</span>
|
||||
</div>
|
||||
<Switch
|
||||
id="edit-isTemplate"
|
||||
checked={formData.isTemplate}
|
||||
onCheckedChange={(checked) => setFormData(prev => ({ ...prev, isTemplate: checked }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 第三行:描述 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="edit-description" className="flex items-center gap-2">
|
||||
<AlignLeft className="h-4 w-4 text-muted-foreground" />
|
||||
表单描述
|
||||
</Label>
|
||||
<Textarea
|
||||
id="edit-description"
|
||||
placeholder="简要说明此表单的用途和填写注意事项..."
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
||||
className="min-h-[100px] resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleClose} disabled={submitting}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>
|
||||
{submitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
||||
保存更改
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditBasicInfoModal;
|
||||
|
||||
@ -10,21 +10,29 @@ import { Switch } from '@/components/ui/switch';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { FileText, Tag, Folder, AlignLeft, Copy, Loader2 } from 'lucide-react';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { createDefinition } from '../service';
|
||||
import { createDefinition, updateDefinition } from '../service';
|
||||
import { getEnabledCategories } from '../../Category/service';
|
||||
import type { FormDefinitionRequest } from '../types';
|
||||
import type { FormDefinitionRequest, FormDefinitionResponse } from '../types';
|
||||
import type { FormCategoryResponse } from '../../Category/types';
|
||||
|
||||
interface CreateModalProps {
|
||||
interface FormBasicInfoModalProps {
|
||||
mode: 'create' | 'edit';
|
||||
visible: boolean;
|
||||
record?: FormDefinitionResponse | null;
|
||||
onClose: () => void;
|
||||
onSuccess: (id: number) => void;
|
||||
onSuccess: (id?: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单定义创建弹窗(第一步:输入基本信息)
|
||||
* 表单定义基本信息弹窗(统一的创建/编辑组件)
|
||||
*/
|
||||
const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }) => {
|
||||
const FormBasicInfoModal: React.FC<FormBasicInfoModalProps> = ({
|
||||
mode,
|
||||
visible,
|
||||
record,
|
||||
onClose,
|
||||
onSuccess
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
@ -37,12 +45,26 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
isTemplate: false,
|
||||
});
|
||||
|
||||
// 加载分类列表
|
||||
// 加载分类列表和初始化数据
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
loadCategories();
|
||||
if (mode === 'edit' && record) {
|
||||
setFormData({
|
||||
name: record.name,
|
||||
key: record.key,
|
||||
categoryId: record.categoryId,
|
||||
description: record.description || '',
|
||||
isTemplate: record.isTemplate || false,
|
||||
});
|
||||
} else if (mode === 'create') {
|
||||
resetForm();
|
||||
}
|
||||
} else {
|
||||
// 弹窗关闭时重置表单数据
|
||||
resetForm();
|
||||
}
|
||||
}, [visible]);
|
||||
}, [visible, mode, record?.id]); // 只依赖 record.id 而不是整个 record 对象
|
||||
|
||||
const loadCategories = async () => {
|
||||
try {
|
||||
@ -65,9 +87,11 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
onClose();
|
||||
const handleClose = (open: boolean) => {
|
||||
// 当 Dialog 的 open 状态变为 false 时才执行关闭逻辑
|
||||
if (!open && !submitting) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
@ -102,54 +126,81 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
// 创建表单定义(只保存基本信息,schema 为空)
|
||||
const request: FormDefinitionRequest = {
|
||||
name: formData.name,
|
||||
key: formData.key,
|
||||
categoryId: formData.categoryId,
|
||||
description: formData.description,
|
||||
isTemplate: formData.isTemplate,
|
||||
status: 'DRAFT',
|
||||
schema: {
|
||||
version: '1.0',
|
||||
formConfig: {
|
||||
labelAlign: 'right',
|
||||
size: 'middle'
|
||||
},
|
||||
fields: []
|
||||
}
|
||||
};
|
||||
if (mode === 'create') {
|
||||
// 创建模式
|
||||
const request: FormDefinitionRequest = {
|
||||
name: formData.name,
|
||||
key: formData.key,
|
||||
categoryId: formData.categoryId,
|
||||
description: formData.description,
|
||||
isTemplate: formData.isTemplate,
|
||||
status: 'DRAFT',
|
||||
schema: {
|
||||
version: '1.0',
|
||||
formConfig: {
|
||||
labelAlign: 'right',
|
||||
size: 'middle'
|
||||
},
|
||||
fields: []
|
||||
}
|
||||
};
|
||||
|
||||
const result = await createDefinition(request);
|
||||
|
||||
toast({
|
||||
title: '创建成功',
|
||||
description: '表单基本信息已保存,现在可以开始设计表单'
|
||||
});
|
||||
const result = await createDefinition(request);
|
||||
|
||||
toast({
|
||||
title: '创建成功',
|
||||
description: '表单基本信息已保存,现在可以开始设计表单'
|
||||
});
|
||||
|
||||
// 跳转到设计器页面
|
||||
navigate(`/form/definitions/${result.id}/design`);
|
||||
handleClose();
|
||||
onSuccess(result.id);
|
||||
} catch (error) {
|
||||
console.error('创建表单失败:', error);
|
||||
toast({
|
||||
title: '创建失败',
|
||||
description: error instanceof Error ? error.message : '创建表单失败',
|
||||
variant: 'destructive'
|
||||
});
|
||||
// 跳转到设计器页面
|
||||
navigate(`/workflow/form/${result.id}/design`);
|
||||
onClose();
|
||||
onSuccess(result.id);
|
||||
} else {
|
||||
// 编辑模式
|
||||
if (!record) return;
|
||||
|
||||
const request: FormDefinitionRequest = {
|
||||
name: formData.name,
|
||||
key: formData.key,
|
||||
categoryId: formData.categoryId,
|
||||
description: formData.description,
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
isTemplate: formData.isTemplate,
|
||||
version: record.version, // 乐观锁版本号
|
||||
};
|
||||
|
||||
await updateDefinition(record.id, request);
|
||||
toast({
|
||||
title: "更新成功",
|
||||
description: `表单 "${formData.name}" 的基本信息已更新`,
|
||||
});
|
||||
// 只调用 onSuccess,让父组件管理关闭逻辑
|
||||
onSuccess();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`${mode === 'create' ? '创建' : '更新'}表单失败:`, error);
|
||||
// 错误提示已在 request.ts 中统一处理,这里只做日志记录和状态重置
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 根据模式调整文案
|
||||
const title = mode === 'create' ? '创建表单定义' : '编辑基本信息';
|
||||
const description = mode === 'create'
|
||||
? '第一步:输入表单的基本信息,然后点击"下一步"进入表单设计器'
|
||||
: '修改表单的名称、标识、分类等基本信息';
|
||||
const submitButtonText = mode === 'create' ? '下一步:设计表单' : '保存更改';
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onOpenChange={handleClose}>
|
||||
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>创建表单定义</DialogTitle>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>
|
||||
第一步:输入表单的基本信息,然后点击"下一步"进入表单设计器
|
||||
{description}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -159,13 +210,13 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
{/* 第一行:表单名称 + 表单标识 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="create-name" className="flex items-center gap-2">
|
||||
<Label htmlFor="form-name" className="flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||
表单名称
|
||||
<span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="create-name"
|
||||
id="form-name"
|
||||
placeholder="例如:员工请假申请表"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
||||
@ -177,13 +228,13 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="create-key" className="flex items-center gap-2">
|
||||
<Label htmlFor="form-key" className="flex items-center gap-2">
|
||||
<Tag className="h-4 w-4 text-muted-foreground" />
|
||||
表单标识
|
||||
<span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="create-key"
|
||||
id="form-key"
|
||||
placeholder="例如:employee-leave-form"
|
||||
value={formData.key}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, key: e.target.value }))}
|
||||
@ -198,7 +249,7 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
{/* 第二行:分类 + 设为模板 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="create-category" className="flex items-center gap-2">
|
||||
<Label htmlFor="form-category" className="flex items-center gap-2">
|
||||
<Folder className="h-4 w-4 text-muted-foreground" />
|
||||
表单分类
|
||||
</Label>
|
||||
@ -206,7 +257,7 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
value={formData.categoryId?.toString() || undefined}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, categoryId: Number(value) }))}
|
||||
>
|
||||
<SelectTrigger id="create-category" className="h-10">
|
||||
<SelectTrigger id="form-category" className="h-10">
|
||||
<SelectValue placeholder="选择表单所属分类" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@ -223,7 +274,7 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="create-isTemplate" className="flex items-center gap-2">
|
||||
<Label htmlFor="form-isTemplate" className="flex items-center gap-2">
|
||||
<Copy className="h-4 w-4 text-muted-foreground" />
|
||||
设为表单模板
|
||||
</Label>
|
||||
@ -232,7 +283,7 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
<span className="text-sm">启用模板功能</span>
|
||||
</div>
|
||||
<Switch
|
||||
id="create-isTemplate"
|
||||
id="form-isTemplate"
|
||||
checked={formData.isTemplate}
|
||||
onCheckedChange={(checked) => setFormData(prev => ({ ...prev, isTemplate: checked }))}
|
||||
/>
|
||||
@ -245,12 +296,12 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
|
||||
{/* 第三行:描述 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="create-description" className="flex items-center gap-2">
|
||||
<Label htmlFor="form-description" className="flex items-center gap-2">
|
||||
<AlignLeft className="h-4 w-4 text-muted-foreground" />
|
||||
表单描述
|
||||
</Label>
|
||||
<Textarea
|
||||
id="create-description"
|
||||
id="form-description"
|
||||
placeholder="简要说明此表单的用途和填写注意事项..."
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
||||
@ -263,12 +314,12 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleClose} disabled={submitting}>
|
||||
<Button variant="outline" onClick={() => handleClose(false)} disabled={submitting}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} disabled={submitting}>
|
||||
{submitting && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
|
||||
下一步:设计表单
|
||||
{submitButtonText}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
@ -276,5 +327,5 @@ const CreateModal: React.FC<CreateModalProps> = ({ visible, onClose, onSuccess }
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateModal;
|
||||
export default FormBasicInfoModal;
|
||||
|
||||
@ -29,8 +29,7 @@ import type { FormDefinitionResponse, FormDefinitionStatus } from './types';
|
||||
import type { FormCategoryResponse } from '../Category/types';
|
||||
import type { Page } from '@/types/base';
|
||||
import { DEFAULT_PAGE_SIZE, DEFAULT_CURRENT } from '@/utils/page';
|
||||
import CreateModal from './components/CreateModal';
|
||||
import EditBasicInfoModal from './components/EditBasicInfoModal';
|
||||
import FormBasicInfoModal from './components/FormBasicInfoModal';
|
||||
import CategoryManageDialog from './components/CategoryManageDialog';
|
||||
|
||||
/**
|
||||
@ -136,7 +135,7 @@ const FormDefinitionList: React.FC = () => {
|
||||
|
||||
// 编辑表单设计
|
||||
const handleEdit = (record: FormDefinitionResponse) => {
|
||||
navigate(`/form/definitions/${record.id}/design`);
|
||||
navigate(`/workflow/form/${record.id}/design`);
|
||||
};
|
||||
|
||||
// 编辑基本信息
|
||||
@ -195,7 +194,7 @@ const FormDefinitionList: React.FC = () => {
|
||||
|
||||
// 查看数据
|
||||
const handleViewData = (record: FormDefinitionResponse) => {
|
||||
navigate(`/form/data?formDefinitionId=${record.id}`);
|
||||
navigate(`/workflow/form/data?formDefinitionId=${record.id}`);
|
||||
};
|
||||
|
||||
// 根据分类 ID 获取分类信息
|
||||
@ -601,7 +600,8 @@ const FormDefinitionList: React.FC = () => {
|
||||
)}
|
||||
|
||||
{/* 创建表单弹窗 */}
|
||||
<CreateModal
|
||||
<FormBasicInfoModal
|
||||
mode="create"
|
||||
visible={createModalVisible}
|
||||
onClose={() => setCreateModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
@ -611,15 +611,26 @@ const FormDefinitionList: React.FC = () => {
|
||||
/>
|
||||
|
||||
{/* 编辑基本信息弹窗 */}
|
||||
<EditBasicInfoModal
|
||||
<FormBasicInfoModal
|
||||
mode="edit"
|
||||
visible={editBasicInfoVisible}
|
||||
record={editBasicInfoRecord}
|
||||
onClose={() => {
|
||||
// 先关闭弹窗
|
||||
setEditBasicInfoVisible(false);
|
||||
setEditBasicInfoRecord(null);
|
||||
// 延迟清空 record,等待 Dialog 关闭动画完成(避免遮罩层残留)
|
||||
setTimeout(() => {
|
||||
setEditBasicInfoRecord(null);
|
||||
}, 300);
|
||||
}}
|
||||
onSuccess={() => {
|
||||
loadData();
|
||||
// 先关闭弹窗
|
||||
setEditBasicInfoVisible(false);
|
||||
// 延迟清空 record 和刷新数据,等待 Dialog 关闭动画完成
|
||||
setTimeout(() => {
|
||||
setEditBasicInfoRecord(null);
|
||||
loadData();
|
||||
}, 300);
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@ -89,13 +89,18 @@ const router = createBrowserRouter([
|
||||
path: 'applications',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><ApplicationList/></Suspense>
|
||||
},
|
||||
{
|
||||
path: 'environments',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><EnvironmentList/></Suspense>
|
||||
},
|
||||
{
|
||||
path: 'deployment',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><DeploymentConfigList/></Suspense>
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'resource',
|
||||
children: [
|
||||
{
|
||||
path: 'environments',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><EnvironmentList/></Suspense>
|
||||
},
|
||||
{
|
||||
path: 'jenkins-manager',
|
||||
@ -152,51 +157,6 @@ const router = createBrowserRouter([
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'form',
|
||||
children: [
|
||||
{
|
||||
path: 'definitions',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionList/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'definitions/create',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionDesigner/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'definitions/:id/design',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionDesigner/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'data',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDataList/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'data/:id',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDataDetail/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'workflow',
|
||||
children: [
|
||||
@ -229,6 +189,51 @@ const router = createBrowserRouter([
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'form',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionList/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionDesigner/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: ':id/design',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDefinitionDesigner/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'data',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDataList/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'data/:id',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FormDataDetail/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'node-design',
|
||||
children: [
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import axios, {AxiosRequestConfig, AxiosResponse} from 'axios';
|
||||
import {message} from 'antd';
|
||||
import {toast} from '@/components/ui/use-toast';
|
||||
|
||||
export interface Response<T = any> {
|
||||
code: number;
|
||||
@ -46,7 +46,11 @@ const responseHandler = (response: AxiosResponse<Response<any>>) => {
|
||||
return result.data;
|
||||
} else {
|
||||
if (result.message != undefined) {
|
||||
message.error(result.message || defaultErrorMessage);
|
||||
toast({
|
||||
title: '操作失败',
|
||||
description: result.message || defaultErrorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
return Promise.reject(response);
|
||||
}
|
||||
return Promise.reject(response);
|
||||
@ -56,7 +60,11 @@ const responseHandler = (response: AxiosResponse<Response<any>>) => {
|
||||
|
||||
const errorHandler = (error: any) => {
|
||||
if (!error.response) {
|
||||
message.error('网络连接异常,请检查网络');
|
||||
toast({
|
||||
title: '网络错误',
|
||||
description: '网络连接异常,请检查网络',
|
||||
variant: 'destructive'
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@ -70,7 +78,11 @@ const errorHandler = (error: any) => {
|
||||
case 401:
|
||||
// 登录已过期,清除所有本地存储并跳转到登录页
|
||||
errorMessage = '登录已过期,请重新登录';
|
||||
message.error(errorMessage);
|
||||
toast({
|
||||
title: '登录已过期',
|
||||
description: errorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
|
||||
// 清除本地存储的所有用户相关信息
|
||||
localStorage.removeItem('token');
|
||||
@ -85,19 +97,35 @@ const errorHandler = (error: any) => {
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = '拒绝访问';
|
||||
message.error(errorMessage);
|
||||
toast({
|
||||
title: '访问被拒绝',
|
||||
description: errorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
break;
|
||||
case 404:
|
||||
errorMessage = '请求错误,未找到该资源';
|
||||
message.error(errorMessage);
|
||||
toast({
|
||||
title: '请求错误',
|
||||
description: errorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
break;
|
||||
case 500:
|
||||
errorMessage = '服务异常,请稍后再试';
|
||||
message.error(errorMessage);
|
||||
toast({
|
||||
title: '服务器错误',
|
||||
description: errorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
break;
|
||||
default:
|
||||
errorMessage = '服务器异常,请稍后再试!';
|
||||
message.error(errorMessage);
|
||||
toast({
|
||||
title: '请求失败',
|
||||
description: errorMessage,
|
||||
variant: 'destructive'
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user