|
|
@@ -1,12 +1,13 @@
|
|
|
import React, { useState, useEffect, useMemo } from 'react';
|
|
|
import { ChevronDown, ChevronUp, Clock, PlayCircle, CheckCircle, AlertTriangle, List, Plus, RefreshCw, AlertCircle, FileText, Rocket, Eye } from 'lucide-react';
|
|
|
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, AreaChart, Area } from 'recharts';
|
|
|
-import { Card, Collapse, Modal, Form as AntdForm, Input, Button, message, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload, Alert, Space } from 'antd';
|
|
|
-import { UploadOutlined, LockOutlined, CheckCircleOutlined } from '@ant-design/icons';
|
|
|
+import { Card, Collapse, Modal, Form as AntdForm, Input, Button, message, Select, DatePicker, InputNumber, Switch, Radio, Checkbox, Cascader, Upload, Alert, Space, Image } from 'antd';
|
|
|
+import { UploadOutlined, LockOutlined, CheckCircleOutlined, BarcodeOutlined, SendOutlined, KeyOutlined } from '@ant-design/icons';
|
|
|
const { Panel } = Collapse;
|
|
|
import { cockpitApi, CockpitStatisticsVO, JobVO, TaskVO } from '../api/cockpit';
|
|
|
import { dictDataApi, DictDataVO } from '../api/DictData';
|
|
|
import { taskManagementApi, MyTaskVO, MyTaskPageParam, MyTaskNodeDetailVO, UpdateNodeApprovalParam } from '../api/mytask';
|
|
|
+import { fileApi } from '../api/file';
|
|
|
import { managerHomeApi, ManagerWorkCountVO, ManagerWorkItemVO, ManagerDayWorkCountVO } from '../api/managerHome';
|
|
|
import { dateFormatter } from '../utils/formatTime';
|
|
|
import { toast } from 'sonner';
|
|
|
@@ -187,6 +188,9 @@ export default function Dashboard() {
|
|
|
const [approvalLoading, setApprovalLoading] = useState(false);
|
|
|
const [submitLoading, setSubmitLoading] = useState(false);
|
|
|
const [showOverdueJobs, setShowOverdueJobs] = useState(false); // 默认隐藏逾期作业
|
|
|
+ const [isolationDeviceNumber, setIsolationDeviceNumber] = useState('');
|
|
|
+ const [isolationFileList, setIsolationFileList] = useState<any[]>([]);
|
|
|
+ const [isolationSubmitLoading, setIsolationSubmitLoading] = useState(false);
|
|
|
|
|
|
// 获取统计数据
|
|
|
const fetchStatistics = async () => {
|
|
|
@@ -1217,6 +1221,15 @@ export default function Dashboard() {
|
|
|
(typeof detailDataWithWorkInfo.formData === 'object' && Object.keys(detailDataWithWorkInfo.formData).length > 0)
|
|
|
);
|
|
|
|
|
|
+ // 打开盲板/拆除详情时先清空设备编号和附件,后续若有 formData 再回填
|
|
|
+ if (isIsolation || isReleaseIsolation) {
|
|
|
+ const isolationType = String(detailDataWithWorkInfo.isolationType ?? '').trim();
|
|
|
+ if (isolationType === '0' || isolationType === '2') {
|
|
|
+ setIsolationDeviceNumber('');
|
|
|
+ setIsolationFileList([]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 如果任务状态为"已通过"且有 formData,则从 formData 中解析表单结构
|
|
|
if (isApproved && hasFormData) {
|
|
|
console.log('Dashboard: ✅ 任务已通过,从 formData 中解析表单结构');
|
|
|
@@ -1303,6 +1316,21 @@ export default function Dashboard() {
|
|
|
taskDetailForm.setFieldsValue(convertedFormValues);
|
|
|
console.log('Dashboard: 表单数据已回填(从 formData)', convertedFormValues);
|
|
|
}, 100);
|
|
|
+ } else if (
|
|
|
+ parsedFormData.deviceNumber !== undefined ||
|
|
|
+ (Array.isArray(parsedFormData.attachments) && parsedFormData.attachments.length > 0)
|
|
|
+ ) {
|
|
|
+ setIsolationDeviceNumber(parsedFormData.deviceNumber ?? '');
|
|
|
+ const list = Array.isArray(parsedFormData.attachments) ? parsedFormData.attachments : [];
|
|
|
+ setIsolationFileList(
|
|
|
+ list.map((item: any, idx: number) => ({
|
|
|
+ uid: `echo-${idx}-${item.url || item.name || idx}`,
|
|
|
+ name: item.name || `文件${idx + 1}`,
|
|
|
+ url: item.url || item.response,
|
|
|
+ status: 'done',
|
|
|
+ response: item.url || item.response,
|
|
|
+ }))
|
|
|
+ );
|
|
|
} else {
|
|
|
console.warn('Dashboard: formData 中缺少 conf 或 fields', { conf: !!conf, fields: !!fields });
|
|
|
message.warning(t('cockpit.formDataIncomplete'));
|
|
|
@@ -2332,6 +2360,8 @@ export default function Dashboard() {
|
|
|
setOriginalConf('');
|
|
|
taskDetailForm.resetFields();
|
|
|
setApprovalComment('');
|
|
|
+ setIsolationDeviceNumber('');
|
|
|
+ setIsolationFileList([]);
|
|
|
}}
|
|
|
footer={null}
|
|
|
width={1000}
|
|
|
@@ -2403,47 +2433,174 @@ export default function Dashboard() {
|
|
|
|
|
|
// 隔离/方案节点、解除隔离节点和还锁节点
|
|
|
if (isIsolation || isReleaseIsolation || isReturnLock) {
|
|
|
+ const isolationType = String(taskDetailData?.isolationType ?? '').trim();
|
|
|
+ const isLockoutTagout = isolationType === '1';
|
|
|
+ const isBlindPlate = isolationType === '0';
|
|
|
+ const isDismantle = isolationType === '2';
|
|
|
+ const showLockCabinet = isReturnLock || (isLockoutTagout && (isIsolation || isReleaseIsolation));
|
|
|
+ const showDeviceForm = (isIsolation || isReleaseIsolation) && (isBlindPlate || isDismantle);
|
|
|
+
|
|
|
+ if (showDeviceForm) {
|
|
|
+ const deviceLabel = isReleaseIsolation
|
|
|
+ ? (isBlindPlate ? t('form.releaseBlindPlateDeviceNo') : t('form.restoreDeviceNo'))
|
|
|
+ : (isBlindPlate ? t('form.blindPlateDeviceNo') : t('form.dismantleDeviceNo'));
|
|
|
+ return (
|
|
|
+ <div key="isolation-device-form" style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
|
|
|
+ <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
|
|
|
+ <Card
|
|
|
+ style={{
|
|
|
+ width: '100%',
|
|
|
+ maxWidth: 500,
|
|
|
+ boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
|
|
|
+ borderRadius: 16,
|
|
|
+ border: '1px solid rgba(22, 119, 255, 0.15)',
|
|
|
+ overflow: 'hidden',
|
|
|
+ background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
|
|
|
+ }}
|
|
|
+ bodyStyle={{ padding: 0 }}
|
|
|
+ >
|
|
|
+ <div style={{ padding: '24px 32px 28px' }}>
|
|
|
+ <div style={{ marginBottom: 26 }}>
|
|
|
+ <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
|
|
|
+ <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
|
+ <BarcodeOutlined style={{ fontSize: 14, color: '#1677ff' }} />
|
|
|
+ </span>
|
|
|
+ {deviceLabel}
|
|
|
+ </label>
|
|
|
+ <Input
|
|
|
+ value={isolationDeviceNumber}
|
|
|
+ onChange={(e) => !isApproved && setIsolationDeviceNumber(e.target.value)}
|
|
|
+ placeholder={`请输入${deviceLabel}`}
|
|
|
+ maxLength={100}
|
|
|
+ size="large"
|
|
|
+ disabled={isApproved}
|
|
|
+ prefix={<BarcodeOutlined style={{ color: '#91caff', marginRight: 8 }} />}
|
|
|
+ style={{ borderRadius: 10, borderColor: '#d9e8ff', fontSize: 15 }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 14, fontWeight: 600, color: '#1f2937', marginBottom: 12 }}>
|
|
|
+ <span style={{ width: 28, height: 28, borderRadius: 8, background: '#e6f4ff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
|
+ <UploadOutlined style={{ fontSize: 14, color: '#1677ff' }} />
|
|
|
+ </span>
|
|
|
+ 附件上传
|
|
|
+ {!isApproved && <span style={{ marginLeft: 8, fontSize: 12, fontWeight: 400, color: '#69b1ff', background: '#e6f4ff', padding: '2px 8px', borderRadius: 6 }}>支持拖拽</span>}
|
|
|
+ </label>
|
|
|
+ {isApproved && isolationFileList.length > 0 ? (
|
|
|
+ <div style={{ borderRadius: 12, padding: '32px 20px', background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)', border: '2px solid #91caff', minHeight: 180, display: 'flex', flexWrap: 'wrap', gap: 16, alignItems: 'center', justifyContent: 'center', alignContent: 'center' }}>
|
|
|
+ <Image.PreviewGroup>
|
|
|
+ {isolationFileList.filter((f: any) => /\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String((f.url || f.response) || '')) || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''))).map((file: any) => (
|
|
|
+ <Image key={file.uid} width={160} height={160} src={file.url || file.response} style={{ objectFit: 'cover', borderRadius: 10, cursor: 'pointer', flexShrink: 0 }} alt={file.name} />
|
|
|
+ ))}
|
|
|
+ </Image.PreviewGroup>
|
|
|
+ {isolationFileList.filter((f: any) => !/\.(jpg|jpeg|png|gif|webp|bmp)(\?|$)/i.test(String((f.url || f.response) || '')) && !/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(String(f.name || ''))).map((file: any) => (
|
|
|
+ <a key={file.uid} href={file.url || file.response} target="_blank" rel="noopener noreferrer" style={{ display: 'block', padding: '12px 16px', background: '#fff', borderRadius: 10, color: '#1677ff', border: '1px solid #91caff', maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{file.name}</a>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ ) : isApproved ? (
|
|
|
+ <div style={{ padding: 24, textAlign: 'center', color: '#8c8c8c', background: '#fafafa', borderRadius: 12 }}>暂无附件</div>
|
|
|
+ ) : (
|
|
|
+ <Upload.Dragger
|
|
|
+ fileList={isolationFileList}
|
|
|
+ onChange={({ fileList }) => setIsolationFileList(fileList.slice(-5))}
|
|
|
+ multiple
|
|
|
+ showUploadList={{ showRemoveIcon: true }}
|
|
|
+ customRequest={async ({ file, onSuccess, onError }) => {
|
|
|
+ try {
|
|
|
+ const url = await fileApi.upload(file as File);
|
|
|
+ onSuccess?.(url);
|
|
|
+ } catch (e: any) {
|
|
|
+ onError?.(e);
|
|
|
+ message.error(e?.message || '文件上传失败');
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ style={{ borderRadius: 12, padding: '32px 20px', background: 'linear-gradient(180deg, #f5faff 0%, #e8f4ff 50%, #e0efff 100%)', border: '2px dashed #91caff' }}
|
|
|
+ >
|
|
|
+ <p className="ant-upload-drag-icon" style={{ marginBottom: 12 }}>
|
|
|
+ <span style={{ width: 64, height: 64, borderRadius: 16, background: 'linear-gradient(135deg, rgba(22,119,255,0.15) 0%, rgba(64,150,255,0.1) 100%)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
|
+ <UploadOutlined style={{ fontSize: 32, color: '#1677ff' }} />
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p className="ant-upload-text" style={{ margin: 0, color: '#0958d9', fontSize: 15, fontWeight: 500 }}>点击或拖拽文件到此区域上传</p>
|
|
|
+ <p className="ant-upload-hint" style={{ margin: '8px 0 0', color: '#69b1ff', fontSize: 13 }}>最多 5 个文件,支持多选</p>
|
|
|
+ </Upload.Dragger>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+ <div className="flex justify-end gap-3" style={{ padding: '20px 24px', borderTop: '1px solid rgba(22, 119, 255, 0.1)', flexShrink: 0, background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)' }}>
|
|
|
+ {!isApproved ? (
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ loading={isolationSubmitLoading}
|
|
|
+ onClick={async () => {
|
|
|
+ const nodeId = taskDetailData.nodeId ?? taskDetailData.id;
|
|
|
+ if (nodeId == null) {
|
|
|
+ message.error(t('cockpit.nodeIdNotExist'));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const uploadedFiles = isolationFileList.filter((f: any) => f.status === 'done' && (f.response != null));
|
|
|
+ const uploadingCount = isolationFileList.filter((f: any) => f.status === 'uploading').length;
|
|
|
+ if (uploadingCount > 0) {
|
|
|
+ message.warning('请等待文件上传完成');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isolationFileList.length > 0 && uploadedFiles.length === 0) {
|
|
|
+ message.warning('请等待文件上传完成或移除未上传成功的文件');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setIsolationSubmitLoading(true);
|
|
|
+ try {
|
|
|
+ const attachmentUrls = uploadedFiles.map((f: any) => {
|
|
|
+ const url = typeof f.response === 'string' ? f.response : (f.response?.url ?? '');
|
|
|
+ return { name: f.name, url };
|
|
|
+ });
|
|
|
+ const payload = { deviceNumber: isolationDeviceNumber, attachments: attachmentUrls };
|
|
|
+ await taskManagementApi.updateNodeApproval({
|
|
|
+ nodeId: typeof nodeId === 'number' ? nodeId : Number(nodeId),
|
|
|
+ approvalStatus: 'approved',
|
|
|
+ formData: JSON.stringify(payload),
|
|
|
+ });
|
|
|
+ message.success(t('common.submit') + t('common.success'));
|
|
|
+ setTaskDetailVisible(false);
|
|
|
+ setTaskDetailData(null);
|
|
|
+ setIsolationDeviceNumber('');
|
|
|
+ setIsolationFileList([]);
|
|
|
+ fetchStatistics();
|
|
|
+ } catch (error: any) {
|
|
|
+ message.error(error?.message || '提交失败');
|
|
|
+ } finally {
|
|
|
+ setIsolationSubmitLoading(false);
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ icon={<SendOutlined />}
|
|
|
+ style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500, boxShadow: '0 4px 14px rgba(22, 119, 255, 0.35)' }}
|
|
|
+ >
|
|
|
+ {t('common.submit')}
|
|
|
+ </Button>
|
|
|
+ ) : (
|
|
|
+ <Button onClick={() => { setTaskDetailVisible(false); setTaskDetailData(null); setIsolationDeviceNumber(''); setIsolationFileList([]); }} style={{ minWidth: 120, height: 44, borderRadius: 10, fontSize: 15, fontWeight: 500 }}>
|
|
|
+ {t('common.cancel')}
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
return (
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: 'flex',
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- minHeight: '400px',
|
|
|
- padding: '40px 20px'
|
|
|
- }}
|
|
|
- >
|
|
|
- <Card
|
|
|
- style={{
|
|
|
- width: '100%',
|
|
|
- maxWidth: '500px',
|
|
|
- textAlign: 'center',
|
|
|
- borderRadius: '12px',
|
|
|
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
|
|
- border: '1px solid #e8e8e8'
|
|
|
- }}
|
|
|
- bodyStyle={{
|
|
|
- padding: '40px 30px'
|
|
|
- }}
|
|
|
- >
|
|
|
- <div style={{ marginBottom: '24px' }}>
|
|
|
- <LockOutlined
|
|
|
- style={{
|
|
|
- fontSize: '64px',
|
|
|
- color: '#1890ff',
|
|
|
- display: 'block'
|
|
|
- }}
|
|
|
- />
|
|
|
+ <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px', background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)' }}>
|
|
|
+ <Card style={{ width: '100%', maxWidth: 500, textAlign: 'center', borderRadius: 16, boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)', border: '1px solid rgba(22, 119, 255, 0.15)', background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)' }} bodyStyle={{ padding: '48px 40px' }}>
|
|
|
+ <div style={{ marginBottom: 28 }}>
|
|
|
+ <span style={{ width: 88, height: 88, borderRadius: 20, background: 'linear-gradient(135deg, #1677ff 0%, #4096ff 50%, #69b1ff 100%)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 8px 24px rgba(22, 119, 255, 0.35)' }}>
|
|
|
+ <LockOutlined style={{ fontSize: 42, color: '#fff' }} />
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: '20px',
|
|
|
- fontWeight: 500,
|
|
|
- color: '#333',
|
|
|
- lineHeight: '1.6'
|
|
|
- }}
|
|
|
- >
|
|
|
- 此节点需要在锁柜系统中进行操作
|
|
|
+ <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6, marginBottom: 8 }}>{t('form.lockCabinetTip')}</div>
|
|
|
+ <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13, color: '#69b1ff' }}>
|
|
|
+ <KeyOutlined />
|
|
|
+ <span>完成取锁、取钥匙后,任务将自动推进</span>
|
|
|
</div>
|
|
|
</Card>
|
|
|
</div>
|
|
|
@@ -2502,47 +2659,50 @@ export default function Dashboard() {
|
|
|
})()}
|
|
|
</div>
|
|
|
) : (
|
|
|
- <div
|
|
|
+ <div
|
|
|
style={{
|
|
|
display: 'flex',
|
|
|
justifyContent: 'center',
|
|
|
alignItems: 'center',
|
|
|
minHeight: '400px',
|
|
|
- padding: '40px 20px'
|
|
|
+ padding: '24px',
|
|
|
+ background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
|
|
|
}}
|
|
|
>
|
|
|
<Card
|
|
|
style={{
|
|
|
width: '100%',
|
|
|
- maxWidth: '500px',
|
|
|
+ maxWidth: 500,
|
|
|
textAlign: 'center',
|
|
|
- borderRadius: '12px',
|
|
|
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
|
|
- border: '1px solid #e8e8e8'
|
|
|
- }}
|
|
|
- bodyStyle={{
|
|
|
- padding: '40px 30px'
|
|
|
+ borderRadius: 16,
|
|
|
+ boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
|
|
|
+ border: '1px solid rgba(22, 119, 255, 0.15)',
|
|
|
+ background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
|
|
|
}}
|
|
|
+ bodyStyle={{ padding: '48px 40px' }}
|
|
|
>
|
|
|
- <div style={{ marginBottom: '24px' }}>
|
|
|
- <CheckCircleOutlined
|
|
|
- style={{
|
|
|
- fontSize: '64px',
|
|
|
- color: '#1890ff',
|
|
|
- display: 'block'
|
|
|
- }}
|
|
|
- />
|
|
|
+ <div style={{ marginBottom: 24 }}>
|
|
|
+ <span
|
|
|
+ style={{
|
|
|
+ width: 88,
|
|
|
+ height: 88,
|
|
|
+ borderRadius: 20,
|
|
|
+ background: 'linear-gradient(135deg, #52c41a 0%, #73d13d 50%, #95de64 100%)',
|
|
|
+ display: 'inline-flex',
|
|
|
+ alignItems: 'center',
|
|
|
+ justifyContent: 'center',
|
|
|
+ boxShadow: '0 8px 24px rgba(82, 196, 26, 0.35)',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <CheckCircleOutlined style={{ fontSize: 42, color: '#fff' }} />
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: '20px',
|
|
|
- fontWeight: 500,
|
|
|
- color: '#333',
|
|
|
- lineHeight: '1.6'
|
|
|
- }}
|
|
|
- >
|
|
|
+ <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
|
|
|
无需填写表单,直接点击底部按钮提交即可
|
|
|
</div>
|
|
|
+ <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
|
|
|
+ 确认无误后点击「完成」结束本节点
|
|
|
+ </div>
|
|
|
</Card>
|
|
|
</div>
|
|
|
)}
|
|
|
@@ -2782,58 +2942,64 @@ export default function Dashboard() {
|
|
|
})()}
|
|
|
</div>
|
|
|
) : (
|
|
|
- <div
|
|
|
+ <div
|
|
|
style={{
|
|
|
+ flex: 1,
|
|
|
+ minHeight: 0,
|
|
|
+ overflow: 'hidden',
|
|
|
display: 'flex',
|
|
|
- justifyContent: 'center',
|
|
|
alignItems: 'center',
|
|
|
- minHeight: '400px',
|
|
|
- padding: '40px 20px'
|
|
|
+ justifyContent: 'center',
|
|
|
+ padding: '24px',
|
|
|
+ background: 'linear-gradient(165deg, #f0f7ff 0%, #fafbff 45%, #fff 100%)',
|
|
|
}}
|
|
|
>
|
|
|
<Card
|
|
|
style={{
|
|
|
width: '100%',
|
|
|
- maxWidth: '500px',
|
|
|
+ maxWidth: 500,
|
|
|
textAlign: 'center',
|
|
|
- borderRadius: '12px',
|
|
|
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
|
|
- border: '1px solid #e8e8e8'
|
|
|
- }}
|
|
|
- bodyStyle={{
|
|
|
- padding: '40px 30px'
|
|
|
+ borderRadius: 16,
|
|
|
+ boxShadow: '0 8px 32px rgba(22, 119, 255, 0.12), 0 2px 8px rgba(0,0,0,0.04)',
|
|
|
+ border: '1px solid rgba(22, 119, 255, 0.15)',
|
|
|
+ background: 'linear-gradient(180deg, #ffffff 0%, #fafcff 100%)',
|
|
|
}}
|
|
|
+ bodyStyle={{ padding: '48px 40px' }}
|
|
|
>
|
|
|
- <div style={{ marginBottom: '24px' }}>
|
|
|
- <CheckCircleOutlined
|
|
|
- style={{
|
|
|
- fontSize: '64px',
|
|
|
- color: '#1890ff',
|
|
|
- display: 'block'
|
|
|
- }}
|
|
|
- />
|
|
|
+ <div style={{ marginBottom: 24 }}>
|
|
|
+ <span
|
|
|
+ style={{
|
|
|
+ width: 88,
|
|
|
+ height: 88,
|
|
|
+ borderRadius: 20,
|
|
|
+ background: 'linear-gradient(135deg, #52c41a 0%, #73d13d 50%, #95de64 100%)',
|
|
|
+ display: 'inline-flex',
|
|
|
+ alignItems: 'center',
|
|
|
+ justifyContent: 'center',
|
|
|
+ boxShadow: '0 8px 24px rgba(82, 196, 26, 0.35)',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <CheckCircleOutlined style={{ fontSize: 42, color: '#fff' }} />
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: '20px',
|
|
|
- fontWeight: 500,
|
|
|
- color: '#333',
|
|
|
- lineHeight: '1.6'
|
|
|
- }}
|
|
|
- >
|
|
|
+ <div style={{ fontSize: 18, fontWeight: 600, color: '#1f2937', lineHeight: 1.6 }}>
|
|
|
无需填写表单,直接点击底部按钮提交即可
|
|
|
</div>
|
|
|
+ <div style={{ marginTop: 12, fontSize: 13, color: '#69b1ff' }}>
|
|
|
+ 确认无误后点击「完成」结束本节点
|
|
|
+ </div>
|
|
|
</Card>
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
{/* 底部按钮 - 取消 + 提交/完成 */}
|
|
|
- <div
|
|
|
+ <div
|
|
|
className="flex justify-end gap-3"
|
|
|
style={{
|
|
|
- padding: '16px 24px',
|
|
|
- borderTop: '1px solid #f0f0f0',
|
|
|
- flexShrink: 0
|
|
|
+ padding: '20px 24px',
|
|
|
+ borderTop: '1px solid rgba(22, 119, 255, 0.1)',
|
|
|
+ flexShrink: 0,
|
|
|
+ background: 'linear-gradient(0deg, #f0f7ff 0%, #fafcff 60%, #fff 100%)',
|
|
|
}}
|
|
|
>
|
|
|
<Button
|