增加审批组件

This commit is contained in:
dengqichen 2025-10-25 12:20:43 +08:00
parent f05e6a55fd
commit a4ab7a6016
7 changed files with 160 additions and 153 deletions

View File

@ -206,7 +206,6 @@ const FormRenderer = forwardRef<FormRendererRef, FormRendererProps>((props, ref)
const handleReset = () => {
form.resetFields();
setFormData({});
message.info('表单已重置');
};
// 监听表单值变化

View File

@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport
ref={ref}
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
)}
{...props}

View File

@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { FormRenderer } from '@/components/FormDesigner';
import Editor from '@/components/Editor';
import { ArrowLeft, Loader2 } from 'lucide-react';
import { getFormDataById } from './service';
import type { FormDataResponse, FormDataStatus, FormDataBusinessType } from './types';
@ -100,48 +100,59 @@ const FormDataDetail: React.FC = () => {
return (
<div className="p-6">
<Card className="mb-4">
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle></CardTitle>
<Button variant="outline" onClick={handleBack}>
<ArrowLeft className="h-4 w-4 mr-2" />
</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>
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-bold"></h2>
<Button variant="outline" onClick={handleBack}>
<ArrowLeft className="h-4 w-4 mr-2" />
</Button>
</div>
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
{/* 使用 FormRenderer 以只读模式展示数据 */}
<FormRenderer
schema={data.schemaSnapshot}
value={data.data}
readonly={true}
showSubmit={false}
showCancel={true}
cancelText="返回"
onCancel={handleBack}
/>
</CardContent>
</Card>
<div className="grid grid-cols-2 gap-4">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</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>
<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>
);
};

View File

@ -294,13 +294,13 @@ const FormDataList: React.FC = () => {
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[180px]"></TableHead>
<TableHead className="w-[240px]"></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[100px]"></TableHead>
<TableHead className="w-[150px]"></TableHead>
<TableHead className="w-[100px]"></TableHead>
<TableHead className="w-[180px]"></TableHead>
<TableHead className="w-[100px]"></TableHead>
<TableHead className="w-[140px]"></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>

View File

@ -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 { Modal, Button as AntButton } from 'antd';
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/card';
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table';
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 { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '@/components/ui/dialog';
import { DataTablePagination } from '@/components/ui/pagination';
import { FormRenderer } from '@/components/FormDesigner';
import { FormRenderer, type FormRendererRef } from '@/components/FormDesigner';
import {
Loader2, Plus, Search, Eye, Edit, FileText, Ban, Trash2,
Database, MoreHorizontal, CheckCircle2, XCircle, Clock, AlertCircle,
@ -61,6 +62,7 @@ const FormDefinitionList: React.FC = () => {
// 预览弹窗
const [previewVisible, setPreviewVisible] = useState(false);
const [previewForm, setPreviewForm] = useState<FormDefinitionResponse | null>(null);
const previewFormRef = useRef<FormRendererRef>(null);
// 删除确认弹窗
const [deleteVisible, setDeleteVisible] = useState(false);
@ -522,26 +524,30 @@ const FormDefinitionList: React.FC = () => {
{/* 预览弹窗 */}
{previewForm && (
<Dialog open={previewVisible} onOpenChange={setPreviewVisible}>
<DialogContent className="max-w-4xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
{previewForm.name} - {previewForm.key} (v{previewForm.formVersion})
</DialogDescription>
</DialogHeader>
<Separator className="my-4" />
<div className="py-4">
<FormRenderer
schema={previewForm.schema}
value={{}}
readonly={true}
showSubmit={false}
onCancel={() => setPreviewVisible(false)}
/>
</div>
</DialogContent>
</Dialog>
<Modal
title={`预览表单 - ${previewForm.name}`}
open={previewVisible}
onCancel={() => setPreviewVisible(false)}
width={800}
footer={[
<AntButton
key="close"
onClick={() => setPreviewVisible(false)}
>
</AntButton>,
]}
>
<div className="text-sm text-muted-foreground mb-4">
{previewForm.key} (v{previewForm.formVersion})
</div>
<FormRenderer
ref={previewFormRef}
schema={previewForm.schema}
value={{}}
readonly={true}
/>
</Modal>
)}
{/* 删除确认弹窗 */}

View File

@ -859,7 +859,6 @@ const handleSubmit = async () => {
</Button>,
]}
destroyOnClose
>
<FormRenderer
ref={modalFormRef}

View File

@ -4,21 +4,9 @@
*/
import React, { useRef, useState } from 'react';
import { Modal } 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 { Modal, Button } from 'antd';
import { FormRenderer, type FormRendererRef } from '@/components/FormDesigner';
import { useToast } from '@/components/ui/use-toast';
import type { FormDefinitionResponse } from '@/pages/Form/Definition/types';
import type { WorkflowDefinition } from '../types';
@ -44,7 +32,7 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
}) => {
const formRef = useRef<FormRendererRef>(null);
const [loading, setLoading] = useState(false);
const [showCancelConfirm, setShowCancelConfirm] = useState(false);
const { toast } = useToast();
// 处理提交
const handleSubmit = async (formData: Record<string, any>) => {
@ -69,16 +57,18 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
};
};
// 处理取消
// 处理取消 - 直接关闭弹窗
const handleCancel = () => {
setShowCancelConfirm(true);
onClose();
};
// 确认取消
const confirmCancel = () => {
// 处理重置
const handleReset = () => {
formRef.current?.reset();
setShowCancelConfirm(false);
onClose();
toast({
title: "表单已重置",
description: "所有字段已恢复为初始状态",
});
};
// 处理表单提交触发
@ -100,72 +90,74 @@ const StartWorkflowModal: React.FC<StartWorkflowModalProps> = ({
const { schema } = formDefinition;
return (
<>
<Modal
title={formDefinition.name || `启动工作流:${workflowDefinition.name}`}
open={open}
onCancel={handleCancel}
width={schema.formConfig.formWidth || 600}
footer={
<div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
<Button variant="outline" onClick={() => formRef.current?.reset()}>
</Button>
<Button variant="outline" onClick={handleCancel}>
</Button>
<Button
onClick={handleTriggerSubmit}
disabled={loading}
>
{loading ? '启动中...' : '启动工作流'}
</Button>
<Modal
title={
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '40px' }}>
<span>{formDefinition.name || `启动工作流:${workflowDefinition.name}`}</span>
<div style={{ display: 'flex', gap: '8px', fontSize: '12px' }}>
<span style={{
background: '#f5f5f5',
padding: '2px 10px',
borderRadius: '12px',
color: '#666',
fontWeight: 'normal'
}}>
v{workflowDefinition.flowVersion}
</span>
<span style={{
background: '#f5f5f5',
padding: '2px 10px',
borderRadius: '12px',
color: '#666',
fontWeight: 'normal'
}}>
v{formDefinition.formVersion}
</span>
</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>
</Modal>
{/* 取消确认对话框 - 设置更高的 z-index 确保在 Modal 之上 */}
<AlertDialog open={showCancelConfirm} onOpenChange={setShowCancelConfirm}>
<AlertDialogPortal>
<AlertDialogOverlay style={{ zIndex: 1050 }} />
<AlertDialogContent style={{ zIndex: 1051 }}>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setShowCancelConfirm(false)}></AlertDialogCancel>
<AlertDialogAction onClick={confirmCancel}></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialog>
</>
}
open={open}
onCancel={handleCancel}
width={schema.formConfig.formWidth || 600}
centered
footer={
<div style={{ display: 'flex', justifyContent: 'center', gap: '12px' }}>
<Button
key="reset"
onClick={handleReset}
style={{ minWidth: '88px' }}
>
</Button>
<Button
key="cancel"
onClick={handleCancel}
style={{ minWidth: '88px' }}
>
</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>
);
};