增加审批组件
This commit is contained in:
parent
f05e6a55fd
commit
a4ab7a6016
@ -206,7 +206,6 @@ const FormRenderer = forwardRef<FormRendererRef, FormRendererProps>((props, ref)
|
|||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setFormData({});
|
setFormData({});
|
||||||
message.info('表单已重置');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听表单值变化
|
// 监听表单值变化
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
|
|||||||
<ToastPrimitives.Viewport
|
<ToastPrimitives.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
|
"fixed top-0 right-0 z-[9999] flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { FormRenderer } from '@/components/FormDesigner';
|
import Editor from '@/components/Editor';
|
||||||
import { ArrowLeft, Loader2 } from 'lucide-react';
|
import { ArrowLeft, Loader2 } from 'lucide-react';
|
||||||
import { getFormDataById } from './service';
|
import { getFormDataById } from './service';
|
||||||
import type { FormDataResponse, FormDataStatus, FormDataBusinessType } from './types';
|
import type { FormDataResponse, FormDataStatus, FormDataBusinessType } from './types';
|
||||||
@ -100,48 +100,59 @@ const FormDataDetail: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<Card className="mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<CardHeader>
|
<h2 className="text-2xl font-bold">表单数据详情</h2>
|
||||||
<div className="flex items-center justify-between">
|
<Button variant="outline" onClick={handleBack}>
|
||||||
<CardTitle>表单数据详情</CardTitle>
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||||
<Button variant="outline" onClick={handleBack}>
|
返回列表
|
||||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
</Button>
|
||||||
返回列表
|
</div>
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-0">
|
|
||||||
<DescriptionItem label="表单标识" value={data.formKey} />
|
|
||||||
<DescriptionItem label="表单版本" value={`v${data.formVersion}`} />
|
|
||||||
<DescriptionItem label="业务类型" value={getBusinessTypeBadge(data.businessType)} />
|
|
||||||
<DescriptionItem label="业务标识" value={data.businessKey || '-'} />
|
|
||||||
<DescriptionItem label="提交人" value={data.submitter || '-'} />
|
|
||||||
<DescriptionItem label="提交时间" value={data.submitTime || '-'} />
|
|
||||||
<DescriptionItem label="状态" value={getStatusBadge(data.status)} />
|
|
||||||
<DescriptionItem label="创建时间" value={data.createTime || '-'} />
|
|
||||||
<DescriptionItem label="更新时间" value={data.updateTime || '-'} />
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle>表单数据</CardTitle>
|
<CardHeader>
|
||||||
</CardHeader>
|
<CardTitle>表单数据详情</CardTitle>
|
||||||
<CardContent>
|
</CardHeader>
|
||||||
{/* 使用 FormRenderer 以只读模式展示数据 */}
|
<CardContent>
|
||||||
<FormRenderer
|
<div className="space-y-0">
|
||||||
schema={data.schemaSnapshot}
|
<DescriptionItem label="表单标识" value={data.formKey} />
|
||||||
value={data.data}
|
<DescriptionItem label="表单版本" value={`v${data.formVersion}`} />
|
||||||
readonly={true}
|
<DescriptionItem label="业务类型" value={getBusinessTypeBadge(data.businessType)} />
|
||||||
showSubmit={false}
|
<DescriptionItem label="业务标识" value={data.businessKey || '-'} />
|
||||||
showCancel={true}
|
<DescriptionItem label="提交人" value={data.submitter || '-'} />
|
||||||
cancelText="返回"
|
<DescriptionItem label="提交时间" value={data.submitTime || '-'} />
|
||||||
onCancel={handleBack}
|
<DescriptionItem label="状态" value={getStatusBadge(data.status)} />
|
||||||
/>
|
<DescriptionItem label="创建时间" value={data.createTime || '-'} />
|
||||||
</CardContent>
|
<DescriptionItem label="更新时间" value={data.updateTime || '-'} />
|
||||||
</Card>
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>表单数据</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="border rounded-md overflow-hidden">
|
||||||
|
<Editor
|
||||||
|
height="600px"
|
||||||
|
language="json"
|
||||||
|
value={JSON.stringify(data.data, null, 2)}
|
||||||
|
options={{
|
||||||
|
readOnly: true,
|
||||||
|
minimap: { enabled: false },
|
||||||
|
fontSize: 14,
|
||||||
|
lineNumbers: 'on',
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
automaticLayout: true,
|
||||||
|
wordWrap: 'on',
|
||||||
|
theme: 'vs-light'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -294,13 +294,13 @@ const FormDataList: React.FC = () => {
|
|||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-[180px]">表单标识</TableHead>
|
<TableHead className="w-[240px]">表单标识</TableHead>
|
||||||
<TableHead className="w-[120px]">分类</TableHead>
|
<TableHead className="w-[120px]">分类</TableHead>
|
||||||
<TableHead className="w-[100px]">业务类型</TableHead>
|
<TableHead className="w-[100px]">业务类型</TableHead>
|
||||||
<TableHead className="w-[150px]">业务标识</TableHead>
|
<TableHead className="w-[150px]">业务标识</TableHead>
|
||||||
<TableHead className="w-[100px]">提交人</TableHead>
|
<TableHead className="w-[100px]">提交人</TableHead>
|
||||||
<TableHead className="w-[180px]">提交时间</TableHead>
|
<TableHead className="w-[180px]">提交时间</TableHead>
|
||||||
<TableHead className="w-[100px]">状态</TableHead>
|
<TableHead className="w-[140px]">状态</TableHead>
|
||||||
<TableHead className="text-right">操作</TableHead>
|
<TableHead className="text-right">操作</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Modal, Button as AntButton } from 'antd';
|
||||||
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/card';
|
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/card';
|
||||||
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table';
|
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@ -8,7 +9,7 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '@/components/ui/dialog';
|
||||||
import { DataTablePagination } from '@/components/ui/pagination';
|
import { DataTablePagination } from '@/components/ui/pagination';
|
||||||
import { FormRenderer } from '@/components/FormDesigner';
|
import { FormRenderer, type FormRendererRef } from '@/components/FormDesigner';
|
||||||
import {
|
import {
|
||||||
Loader2, Plus, Search, Eye, Edit, FileText, Ban, Trash2,
|
Loader2, Plus, Search, Eye, Edit, FileText, Ban, Trash2,
|
||||||
Database, MoreHorizontal, CheckCircle2, XCircle, Clock, AlertCircle,
|
Database, MoreHorizontal, CheckCircle2, XCircle, Clock, AlertCircle,
|
||||||
@ -61,6 +62,7 @@ const FormDefinitionList: React.FC = () => {
|
|||||||
// 预览弹窗
|
// 预览弹窗
|
||||||
const [previewVisible, setPreviewVisible] = useState(false);
|
const [previewVisible, setPreviewVisible] = useState(false);
|
||||||
const [previewForm, setPreviewForm] = useState<FormDefinitionResponse | null>(null);
|
const [previewForm, setPreviewForm] = useState<FormDefinitionResponse | null>(null);
|
||||||
|
const previewFormRef = useRef<FormRendererRef>(null);
|
||||||
|
|
||||||
// 删除确认弹窗
|
// 删除确认弹窗
|
||||||
const [deleteVisible, setDeleteVisible] = useState(false);
|
const [deleteVisible, setDeleteVisible] = useState(false);
|
||||||
@ -522,26 +524,30 @@ const FormDefinitionList: React.FC = () => {
|
|||||||
|
|
||||||
{/* 预览弹窗 */}
|
{/* 预览弹窗 */}
|
||||||
{previewForm && (
|
{previewForm && (
|
||||||
<Dialog open={previewVisible} onOpenChange={setPreviewVisible}>
|
<Modal
|
||||||
<DialogContent className="max-w-4xl max-h-[85vh] overflow-y-auto">
|
title={`预览表单 - ${previewForm.name}`}
|
||||||
<DialogHeader>
|
open={previewVisible}
|
||||||
<DialogTitle>预览表单</DialogTitle>
|
onCancel={() => setPreviewVisible(false)}
|
||||||
<DialogDescription>
|
width={800}
|
||||||
{previewForm.name} - {previewForm.key} (v{previewForm.formVersion})
|
footer={[
|
||||||
</DialogDescription>
|
<AntButton
|
||||||
</DialogHeader>
|
key="close"
|
||||||
<Separator className="my-4" />
|
onClick={() => setPreviewVisible(false)}
|
||||||
<div className="py-4">
|
>
|
||||||
<FormRenderer
|
关闭
|
||||||
schema={previewForm.schema}
|
</AntButton>,
|
||||||
value={{}}
|
]}
|
||||||
readonly={true}
|
>
|
||||||
showSubmit={false}
|
<div className="text-sm text-muted-foreground mb-4">
|
||||||
onCancel={() => setPreviewVisible(false)}
|
{previewForm.key} (v{previewForm.formVersion})
|
||||||
/>
|
</div>
|
||||||
</div>
|
<FormRenderer
|
||||||
</DialogContent>
|
ref={previewFormRef}
|
||||||
</Dialog>
|
schema={previewForm.schema}
|
||||||
|
value={{}}
|
||||||
|
readonly={true}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 删除确认弹窗 */}
|
{/* 删除确认弹窗 */}
|
||||||
|
|||||||
@ -859,7 +859,6 @@ const handleSubmit = async () => {
|
|||||||
保存
|
保存
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
destroyOnClose
|
|
||||||
>
|
>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
ref={modalFormRef}
|
ref={modalFormRef}
|
||||||
|
|||||||
@ -4,21 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { Modal } from 'antd';
|
import { Modal, Button } from 'antd';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogPortal,
|
|
||||||
AlertDialogOverlay,
|
|
||||||
} from '@/components/ui/alert-dialog';
|
|
||||||
import { FormRenderer, type FormRendererRef } from '@/components/FormDesigner';
|
import { FormRenderer, type FormRendererRef } from '@/components/FormDesigner';
|
||||||
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import type { FormDefinitionResponse } from '@/pages/Form/Definition/types';
|
import type { FormDefinitionResponse } from '@/pages/Form/Definition/types';
|
||||||
import type { WorkflowDefinition } from '../types';
|
import type { WorkflowDefinition } from '../types';
|
||||||
|
|
||||||
@ -44,7 +32,7 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const formRef = useRef<FormRendererRef>(null);
|
const formRef = useRef<FormRendererRef>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [showCancelConfirm, setShowCancelConfirm] = useState(false);
|
const { toast } = useToast();
|
||||||
|
|
||||||
// 处理提交
|
// 处理提交
|
||||||
const handleSubmit = async (formData: Record<string, any>) => {
|
const handleSubmit = async (formData: Record<string, any>) => {
|
||||||
@ -69,16 +57,18 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理取消
|
// 处理取消 - 直接关闭弹窗
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setShowCancelConfirm(true);
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 确认取消
|
// 处理重置
|
||||||
const confirmCancel = () => {
|
const handleReset = () => {
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
setShowCancelConfirm(false);
|
toast({
|
||||||
onClose();
|
title: "表单已重置",
|
||||||
|
description: "所有字段已恢复为初始状态",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理表单提交触发
|
// 处理表单提交触发
|
||||||
@ -100,72 +90,74 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
|
|||||||
const { schema } = formDefinition;
|
const { schema } = formDefinition;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal
|
||||||
<Modal
|
title={
|
||||||
title={formDefinition.name || `启动工作流:${workflowDefinition.name}`}
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '40px' }}>
|
||||||
open={open}
|
<span>{formDefinition.name || `启动工作流:${workflowDefinition.name}`}</span>
|
||||||
onCancel={handleCancel}
|
<div style={{ display: 'flex', gap: '8px', fontSize: '12px' }}>
|
||||||
width={schema.formConfig.formWidth || 600}
|
<span style={{
|
||||||
footer={
|
background: '#f5f5f5',
|
||||||
<div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
|
padding: '2px 10px',
|
||||||
<Button variant="outline" onClick={() => formRef.current?.reset()}>
|
borderRadius: '12px',
|
||||||
重置
|
color: '#666',
|
||||||
</Button>
|
fontWeight: 'normal'
|
||||||
<Button variant="outline" onClick={handleCancel}>
|
}}>
|
||||||
取消
|
工作流 v{workflowDefinition.flowVersion}
|
||||||
</Button>
|
</span>
|
||||||
<Button
|
<span style={{
|
||||||
onClick={handleTriggerSubmit}
|
background: '#f5f5f5',
|
||||||
disabled={loading}
|
padding: '2px 10px',
|
||||||
>
|
borderRadius: '12px',
|
||||||
{loading ? '启动中...' : '启动工作流'}
|
color: '#666',
|
||||||
</Button>
|
fontWeight: 'normal'
|
||||||
|
}}>
|
||||||
|
表单 v{formDefinition.formVersion}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
destroyOnClose
|
|
||||||
>
|
|
||||||
<FormRenderer
|
|
||||||
ref={formRef}
|
|
||||||
fields={schema.fields}
|
|
||||||
formConfig={schema.formConfig}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 表单元信息 */}
|
|
||||||
<div style={{
|
|
||||||
marginTop: 16,
|
|
||||||
padding: 12,
|
|
||||||
background: '#f5f5f5',
|
|
||||||
borderRadius: 4,
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#666'
|
|
||||||
}}>
|
|
||||||
<div><strong>工作流标识:</strong>{workflowDefinition.key}</div>
|
|
||||||
<div><strong>工作流版本:</strong>v{workflowDefinition.flowVersion}</div>
|
|
||||||
<div><strong>表单标识:</strong>{formDefinition.key}</div>
|
|
||||||
<div><strong>表单版本:</strong>v{formDefinition.formVersion}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
}
|
||||||
|
open={open}
|
||||||
{/* 取消确认对话框 - 设置更高的 z-index 确保在 Modal 之上 */}
|
onCancel={handleCancel}
|
||||||
<AlertDialog open={showCancelConfirm} onOpenChange={setShowCancelConfirm}>
|
width={schema.formConfig.formWidth || 600}
|
||||||
<AlertDialogPortal>
|
centered
|
||||||
<AlertDialogOverlay style={{ zIndex: 1050 }} />
|
footer={
|
||||||
<AlertDialogContent style={{ zIndex: 1051 }}>
|
<div style={{ display: 'flex', justifyContent: 'center', gap: '12px' }}>
|
||||||
<AlertDialogHeader>
|
<Button
|
||||||
<AlertDialogTitle>确认取消</AlertDialogTitle>
|
key="reset"
|
||||||
<AlertDialogDescription>
|
onClick={handleReset}
|
||||||
工作流将不会启动,确定要取消吗?
|
style={{ minWidth: '88px' }}
|
||||||
</AlertDialogDescription>
|
>
|
||||||
</AlertDialogHeader>
|
重置
|
||||||
<AlertDialogFooter>
|
</Button>
|
||||||
<AlertDialogCancel onClick={() => setShowCancelConfirm(false)}>返回</AlertDialogCancel>
|
<Button
|
||||||
<AlertDialogAction onClick={confirmCancel}>确定取消</AlertDialogAction>
|
key="cancel"
|
||||||
</AlertDialogFooter>
|
onClick={handleCancel}
|
||||||
</AlertDialogContent>
|
style={{ minWidth: '88px' }}
|
||||||
</AlertDialogPortal>
|
>
|
||||||
</AlertDialog>
|
取消
|
||||||
</>
|
</Button>
|
||||||
|
<Button
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
loading={loading}
|
||||||
|
onClick={handleTriggerSubmit}
|
||||||
|
style={{
|
||||||
|
minWidth: '120px',
|
||||||
|
backgroundColor: '#000',
|
||||||
|
borderColor: '#000'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
启动工作流
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FormRenderer
|
||||||
|
ref={formRef}
|
||||||
|
schema={schema}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user