2025-06-28 14:20:17 +08:00

1052 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import {
Card,
Descriptions,
Button,
Tag,
Typography,
Space,
Modal,
Input,
message,
Spin,
Progress,
Tabs,
Image,
Select,
Form,
Alert,
Divider,
Upload,
Rate,
Timeline,
Statistic,
Row,
Col,
Avatar,
} from 'antd';
import {
ArrowLeftOutlined,
FileTextOutlined,
DownloadOutlined,
EyeOutlined,
EditOutlined,
CheckCircleOutlined,
ClockCircleOutlined,
ExclamationCircleOutlined,
FileOutlined,
TranslationOutlined,
UserOutlined,
CalendarOutlined,
DollarOutlined,
StarOutlined,
SettingOutlined,
MessageOutlined,
ReloadOutlined,
DeleteOutlined,
AuditOutlined,
UploadOutlined,
SwapOutlined,
} from '@ant-design/icons';
import { DocumentTranslation } from '../../types';
import { database } from '../../utils/database';
import { api } from '../../utils/api';
const { Title, Text, Paragraph } = Typography;
const { TextArea } = Input;
const { TabPane } = Tabs;
const { Option } = Select;
interface DocumentDetailProps {}
const DocumentDetail: React.FC<DocumentDetailProps> = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [document, setDocument] = useState<DocumentTranslation | null>(null);
const [loading, setLoading] = useState(true);
const [editModalVisible, setEditModalVisible] = useState(false);
const [statusModalVisible, setStatusModalVisible] = useState(false);
const [reassignModalVisible, setReassignModalVisible] = useState(false);
const [refundModalVisible, setRefundModalVisible] = useState(false);
const [adminNoteModalVisible, setAdminNoteModalVisible] = useState(false);
const [retranslateModalVisible, setRetranslateModalVisible] = useState(false);
const [form] = Form.useForm();
const [statusForm] = Form.useForm();
const [reassignForm] = Form.useForm();
const [refundForm] = Form.useForm();
const [noteForm] = Form.useForm();
const [retranslateForm] = Form.useForm();
useEffect(() => {
if (id) {
loadDocumentDetails();
}
}, [id]);
const loadDocumentDetails = async () => {
try {
setLoading(true);
await database.connect();
// 模拟获取文档详情(管理员视角)
const mockDocument: DocumentTranslation = {
id: id!,
userId: 'user_1',
fileName: 'business_contract.pdf',
originalSize: 2048576,
fileUrl: '/documents/business_contract.pdf',
translatedFileUrl: '/documents/business_contract_translated.pdf',
sourceLanguage: 'zh-CN',
targetLanguage: 'en-US',
status: 'completed',
progress: 100,
quality: 'professional',
urgency: 'normal',
estimatedTime: 120,
actualTime: 105,
cost: 128.50,
translatorId: 'translator_2',
translatorName: '王译员',
rating: 4,
feedback: '翻译质量很好,术语准确,但有个别地方可以更流畅。',
createdAt: '2024-01-15T09:00:00Z',
completedAt: '2024-01-15T10:45:00Z',
// 管理员相关字段
adminNotes: '客户对翻译质量满意,已完成交付',
paymentStatus: 'paid',
refundAmount: 0,
qualityScore: 92,
issues: [],
retranslationCount: 0,
clientName: '李先生',
clientEmail: 'li@example.com',
clientPhone: '+86 138 0013 8000',
};
setDocument(mockDocument);
// 填充表单数据
form.setFieldsValue({
fileName: mockDocument.fileName,
quality: mockDocument.quality,
urgency: mockDocument.urgency,
cost: mockDocument.cost,
translatorName: mockDocument.translatorName,
});
statusForm.setFieldsValue({
status: mockDocument.status,
});
} catch (error) {
console.error('加载文档详情失败:', error);
message.error('加载文档详情失败');
} finally {
setLoading(false);
}
};
const handleEdit = async (values: any) => {
if (!document) return;
try {
const updatedDocument = {
...document,
...values,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setEditModalVisible(false);
message.success('文档信息更新成功');
} catch (error) {
message.error('更新文档信息失败');
}
};
const handleStatusChange = async (values: any) => {
if (!document) return;
try {
const updatedDocument = {
...document,
status: values.status,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setStatusModalVisible(false);
message.success('状态更新成功');
} catch (error) {
message.error('更新状态失败');
}
};
const handleReassign = async (values: any) => {
if (!document) return;
try {
const updatedDocument = {
...document,
translatorId: values.translatorId,
translatorName: values.translatorName,
status: 'in_progress' as const,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setReassignModalVisible(false);
message.success('译员重新分配成功');
} catch (error) {
message.error('重新分配译员失败');
}
};
const handleRefund = async (values: any) => {
if (!document) return;
try {
const refundAmount = values.amount || document.cost;
// 模拟退款API调用
await api.refundPayment(`payment_${document.id}`, refundAmount);
const updatedDocument = {
...document,
refundAmount: refundAmount,
paymentStatus: 'refunded' as const,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setRefundModalVisible(false);
message.success('退款处理成功');
} catch (error) {
message.error('退款处理失败');
}
};
const handleAddAdminNote = async (values: any) => {
if (!document) return;
try {
const updatedDocument = {
...document,
adminNotes: values.note,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setAdminNoteModalVisible(false);
message.success('管理员备注添加成功');
} catch (error) {
message.error('添加备注失败');
}
};
const handleRetranslate = async (values: any) => {
if (!document) return;
try {
const updatedDocument = {
...document,
retranslationCount: (document.retranslationCount || 0) + 1,
status: 'in_progress' as const,
progress: 0,
adminNotes: `${document.adminNotes || ''}\n重新翻译原因: ${values.reason}`,
updatedAt: new Date().toISOString(),
};
setDocument(updatedDocument);
setRetranslateModalVisible(false);
message.success('重新翻译任务已创建');
} catch (error) {
message.error('创建重新翻译任务失败');
}
};
const getStatusColor = (status: string) => {
const colors = {
pending: 'orange',
in_progress: 'blue',
completed: 'green',
cancelled: 'red',
failed: 'red',
};
return colors[status as keyof typeof colors] || 'default';
};
const getStatusText = (status: string) => {
const texts = {
pending: '等待处理',
in_progress: '翻译中',
completed: '已完成',
cancelled: '已取消',
failed: '翻译失败',
};
return texts[status as keyof typeof texts] || status;
};
const getPaymentStatusColor = (status: string) => {
const colors = {
pending: 'orange',
paid: 'green',
refunded: 'purple',
failed: 'red',
};
return colors[status as keyof typeof colors] || 'default';
};
const getPaymentStatusText = (status: string) => {
const texts = {
pending: '待支付',
paid: '已支付',
refunded: '已退款',
failed: '支付失败',
};
return texts[status as keyof typeof texts] || status;
};
const getQualityText = (quality: string) => {
const texts = {
basic: '基础版',
professional: '专业版',
premium: '高级版',
};
return texts[quality as keyof typeof texts] || quality;
};
const getUrgencyText = (urgency: string) => {
const texts = {
normal: '普通',
urgent: '加急',
emergency: '特急',
};
return texts[urgency as keyof typeof texts] || urgency;
};
const formatFileSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '50px' }}>
<Spin size="large" />
<div style={{ marginTop: '16px' }}>...</div>
</div>
);
}
if (!document) {
return (
<div style={{ textAlign: 'center', padding: '50px' }}>
<div></div>
<Button type="primary" onClick={() => navigate('/documents')} style={{ marginTop: '16px' }}>
</Button>
</div>
);
}
return (
<div style={{ padding: '24px' }}>
{/* 头部导航 */}
<div style={{ marginBottom: '24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/documents')}
style={{ marginRight: '16px' }}
>
</Button>
<Title level={2} style={{ display: 'inline-block', margin: 0 }}>
#{document.id}
</Title>
</div>
{/* 管理员操作按钮 */}
<Space>
<Button
icon={<EditOutlined />}
onClick={() => setEditModalVisible(true)}
>
</Button>
<Button
icon={<SettingOutlined />}
onClick={() => setStatusModalVisible(true)}
>
</Button>
<Button
icon={<UserOutlined />}
onClick={() => setReassignModalVisible(true)}
disabled={document.status === 'completed'}
>
</Button>
<Button
icon={<ReloadOutlined />}
onClick={() => setRetranslateModalVisible(true)}
disabled={document.status !== 'completed'}
>
</Button>
<Button
icon={<DollarOutlined />}
onClick={() => setRefundModalVisible(true)}
disabled={document.paymentStatus !== 'paid'}
>
退
</Button>
<Button
icon={<MessageOutlined />}
onClick={() => setAdminNoteModalVisible(true)}
>
</Button>
</Space>
</div>
{/* 系统状态提醒 */}
{document.issues && document.issues.length > 0 && (
<Alert
message="系统检测到问题"
description={document.issues.join(', ')}
type="warning"
showIcon
style={{ marginBottom: '24px' }}
/>
)}
{/* 基本信息卡片 */}
<Card title="文档信息" style={{ marginBottom: '24px' }}>
<Descriptions column={3} bordered>
<Descriptions.Item label="文件名" span={2}>
<Space>
<FileOutlined />
{document.fileName}
</Space>
</Descriptions.Item>
<Descriptions.Item label="文件大小" span={1}>
{formatFileSize(document.originalSize)}
</Descriptions.Item>
<Descriptions.Item label="状态" span={1}>
<Tag color={getStatusColor(document.status)}>
{getStatusText(document.status)}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="支付状态" span={1}>
<Tag color={getPaymentStatusColor(document.paymentStatus)}>
{getPaymentStatusText(document.paymentStatus)}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="翻译进度" span={1}>
<Progress percent={document.progress} size="small" />
</Descriptions.Item>
<Descriptions.Item label="客户姓名" span={1}>
<Space>
<UserOutlined />
{document.clientName}
</Space>
</Descriptions.Item>
<Descriptions.Item label="客户邮箱" span={1}>
{document.clientEmail}
</Descriptions.Item>
<Descriptions.Item label="客户电话" span={1}>
{document.clientPhone}
</Descriptions.Item>
<Descriptions.Item label="源语言" span={1}>
{document.sourceLanguage}
</Descriptions.Item>
<Descriptions.Item label="目标语言" span={1}>
{document.targetLanguage}
</Descriptions.Item>
<Descriptions.Item label="翻译质量" span={1}>
{getQualityText(document.quality)}
</Descriptions.Item>
<Descriptions.Item label="紧急程度" span={1}>
{getUrgencyText(document.urgency)}
</Descriptions.Item>
<Descriptions.Item label="译员" span={1}>
<Space>
<UserOutlined />
{document.translatorName}
</Space>
</Descriptions.Item>
<Descriptions.Item label="重译次数" span={1}>
<Text type={(document.retranslationCount || 0) > 0 ? 'warning' : 'secondary'}>
{document.retranslationCount || 0}
</Text>
</Descriptions.Item>
<Descriptions.Item label="创建时间" span={1}>
<Space>
<CalendarOutlined />
{new Date(document.createdAt).toLocaleString()}
</Space>
</Descriptions.Item>
<Descriptions.Item label="完成时间" span={1}>
<Space>
<CalendarOutlined />
{document.completedAt ? new Date(document.completedAt).toLocaleString() : '-'}
</Space>
</Descriptions.Item>
<Descriptions.Item label="预计时间" span={1}>
{document.estimatedTime}
</Descriptions.Item>
<Descriptions.Item label="实际时间" span={1}>
{document.actualTime || '-'}
</Descriptions.Item>
<Descriptions.Item label="费用" span={1}>
<Space>
<DollarOutlined />
<Text strong>¥{document.cost.toFixed(2)}</Text>
</Space>
</Descriptions.Item>
<Descriptions.Item label="退款金额" span={1}>
<Space>
<DollarOutlined />
<Text type={document.refundAmount > 0 ? 'danger' : 'secondary'}>
¥{document.refundAmount.toFixed(2)}
</Text>
</Space>
</Descriptions.Item>
<Descriptions.Item label="质量评分" span={1}>
<Space>
<AuditOutlined />
<Text strong style={{ color: document.qualityScore >= 90 ? '#52c41a' : document.qualityScore >= 70 ? '#faad14' : '#ff4d4f' }}>
{document.qualityScore}/100
</Text>
</Space>
</Descriptions.Item>
</Descriptions>
{document.adminNotes && (
<div style={{ marginTop: '16px' }}>
<Text strong></Text>
<Paragraph style={{ marginTop: '8px', background: '#f6f6f6', padding: '12px', borderRadius: '6px' }}>
{document.adminNotes}
</Paragraph>
</div>
)}
</Card>
{/* 文件操作卡片 */}
<Card
title={
<Space>
<FileTextOutlined />
</Space>
}
style={{ marginBottom: '24px' }}
>
<div style={{ display: 'flex', justifyContent: 'space-around', textAlign: 'center', padding: '20px' }}>
<div>
<Button
type="primary"
size="large"
icon={<EyeOutlined />}
onClick={() => message.success('预览原文件...')}
style={{ marginBottom: '8px' }}
>
</Button>
<div>
<Text type="secondary"></Text>
</div>
</div>
<div>
<Button
size="large"
icon={<DownloadOutlined />}
onClick={() => message.success('下载原文件中...')}
style={{ marginBottom: '8px' }}
>
</Button>
<div>
<Text type="secondary">{formatFileSize(document.originalSize)}</Text>
</div>
</div>
{document.translatedFileUrl && (
<>
<div>
<Button
type="primary"
size="large"
icon={<EyeOutlined />}
onClick={() => message.success('预览译文...')}
style={{ marginBottom: '8px' }}
>
</Button>
<div>
<Text type="secondary"></Text>
</div>
</div>
<div>
<Button
size="large"
icon={<DownloadOutlined />}
onClick={() => message.success('下载译文中...')}
style={{ marginBottom: '8px' }}
>
</Button>
<div>
<Text type="secondary"></Text>
</div>
</div>
</>
)}
</div>
</Card>
{/* 详细内容标签页 */}
<Card>
<Tabs defaultActiveKey="progress">
<TabPane
tab={
<Space>
<ClockCircleOutlined />
</Space>
}
key="progress"
>
<div style={{ padding: '20px' }}>
<Timeline>
<Timeline.Item color="green">
<div>
<Text strong></Text>
<div style={{ color: '#999', fontSize: '12px' }}>
{new Date(document.createdAt).toLocaleString()}
</div>
</div>
</Timeline.Item>
<Timeline.Item color="blue">
<div>
<Text strong></Text>
<div style={{ color: '#999', fontSize: '12px' }}>
{document.translatorName}
</div>
</div>
</Timeline.Item>
<Timeline.Item color={document.status === 'completed' ? 'green' : 'blue'}>
<div>
<Text strong></Text>
<div style={{ color: '#999', fontSize: '12px' }}>
{document.progress}%
</div>
</div>
</Timeline.Item>
{document.status === 'completed' && (
<Timeline.Item color="green">
<div>
<Text strong></Text>
<div style={{ color: '#999', fontSize: '12px' }}>
{document.completedAt && new Date(document.completedAt).toLocaleString()}
</div>
</div>
</Timeline.Item>
)}
</Timeline>
</div>
</TabPane>
<TabPane
tab={
<Space>
<StarOutlined />
</Space>
}
key="rating"
>
<div style={{ padding: '20px' }}>
{document.rating ? (
<>
<div style={{ marginBottom: '20px' }}>
<Text strong></Text>
<Rate disabled value={document.rating} style={{ marginLeft: '8px' }} />
<Text style={{ marginLeft: '8px' }}>
({document.rating}/5 )
</Text>
</div>
{document.feedback && (
<div>
<Text strong></Text>
<Paragraph style={{ marginTop: '8px' }}>
{document.feedback}
</Paragraph>
</div>
)}
</>
) : (
<div style={{ textAlign: 'center', color: '#999', padding: '50px' }}>
</div>
)}
</div>
</TabPane>
<TabPane
tab={
<Space>
<AuditOutlined />
</Space>
}
key="quality"
>
<div style={{ padding: '20px' }}>
<Descriptions column={2}>
<Descriptions.Item label="质量评分">
<Progress
type="circle"
percent={document.qualityScore}
width={80}
strokeColor={document.qualityScore >= 90 ? '#52c41a' : document.qualityScore >= 70 ? '#faad14' : '#ff4d4f'}
/>
</Descriptions.Item>
<Descriptions.Item label="系统检测">
{document.issues && document.issues.length > 0 ? (
<div>
{document.issues.map((issue, index) => (
<Tag key={index} color="red" style={{ marginBottom: '4px' }}>
{issue}
</Tag>
))}
</div>
) : (
<Tag color="green"></Tag>
)}
</Descriptions.Item>
<Descriptions.Item label="时间效率">
<Statistic
title="实际用时/预计用时"
value={document.actualTime ? (document.actualTime / document.estimatedTime) * 100 : 0}
precision={1}
suffix="%"
valueStyle={{ color: (document.actualTime || 0) <= document.estimatedTime ? '#3f8600' : '#cf1322' }}
/>
</Descriptions.Item>
<Descriptions.Item label="重译情况">
<Text type={(document.retranslationCount || 0) > 0 ? 'warning' : 'secondary'}>
{document.retranslationCount || 0}
</Text>
</Descriptions.Item>
</Descriptions>
</div>
</TabPane>
</Tabs>
</Card>
{/* 编辑信息弹窗 */}
<Modal
title="编辑文档信息"
visible={editModalVisible}
onCancel={() => setEditModalVisible(false)}
footer={null}
width={600}
>
<Form
form={form}
layout="vertical"
onFinish={handleEdit}
>
<Form.Item
name="fileName"
label="文件名"
rules={[{ required: true, message: '请输入文件名' }]}
>
<Input />
</Form.Item>
<Form.Item
name="quality"
label="翻译质量"
rules={[{ required: true, message: '请选择翻译质量' }]}
>
<Select>
<Option value="basic"></Option>
<Option value="professional"></Option>
<Option value="premium"></Option>
</Select>
</Form.Item>
<Form.Item
name="urgency"
label="紧急程度"
rules={[{ required: true, message: '请选择紧急程度' }]}
>
<Select>
<Option value="normal"></Option>
<Option value="urgent"></Option>
<Option value="emergency"></Option>
</Select>
</Form.Item>
<Form.Item
name="cost"
label="费用"
rules={[{ required: true, message: '请输入费用' }]}
>
<Input type="number" addonAfter="元" />
</Form.Item>
<Form.Item
name="translatorName"
label="译员姓名"
>
<Input />
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setEditModalVisible(false)}>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
{/* 更改状态弹窗 */}
<Modal
title="更改文档状态"
visible={statusModalVisible}
onCancel={() => setStatusModalVisible(false)}
footer={null}
>
<Form
form={statusForm}
layout="vertical"
onFinish={handleStatusChange}
>
<Form.Item
name="status"
label="新状态"
rules={[{ required: true, message: '请选择状态' }]}
>
<Select>
<Option value="pending"></Option>
<Option value="in_progress"></Option>
<Option value="completed"></Option>
<Option value="cancelled"></Option>
<Option value="failed"></Option>
</Select>
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setStatusModalVisible(false)}>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
{/* 重新分配译员弹窗 */}
<Modal
title="重新分配译员"
visible={reassignModalVisible}
onCancel={() => setReassignModalVisible(false)}
footer={null}
>
<Form
form={reassignForm}
layout="vertical"
onFinish={handleReassign}
>
<Form.Item
name="translatorId"
label="选择译员"
rules={[{ required: true, message: '请选择译员' }]}
>
<Select>
<Option value="translator_1"> - </Option>
<Option value="translator_2"> - </Option>
<Option value="translator_3"> - </Option>
</Select>
</Form.Item>
<Form.Item
name="translatorName"
label="译员姓名"
rules={[{ required: true, message: '请输入译员姓名' }]}
>
<Input />
</Form.Item>
<Form.Item
name="reason"
label="重新分配原因"
rules={[{ required: true, message: '请输入原因' }]}
>
<TextArea rows={3} placeholder="请输入重新分配原因..." />
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setReassignModalVisible(false)}>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
{/* 重新翻译弹窗 */}
<Modal
title="重新翻译"
visible={retranslateModalVisible}
onCancel={() => setRetranslateModalVisible(false)}
footer={null}
>
<Form
form={retranslateForm}
layout="vertical"
onFinish={handleRetranslate}
>
<Alert
message="重新翻译提醒"
description="重新翻译将重置翻译进度,请确认操作"
type="warning"
style={{ marginBottom: '16px' }}
/>
<Form.Item
name="reason"
label="重新翻译原因"
rules={[{ required: true, message: '请输入重新翻译原因' }]}
>
<TextArea rows={3} placeholder="请输入重新翻译原因..." />
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setRetranslateModalVisible(false)}>
</Button>
<Button type="primary" danger htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
{/* 退款处理弹窗 */}
<Modal
title="处理退款"
visible={refundModalVisible}
onCancel={() => setRefundModalVisible(false)}
footer={null}
>
<Form
form={refundForm}
layout="vertical"
onFinish={handleRefund}
initialValues={{ amount: document.cost }}
>
<Alert
message="退款提醒"
description={`原支付金额:¥${document.cost.toFixed(2)}`}
type="info"
style={{ marginBottom: '16px' }}
/>
<Form.Item
name="amount"
label="退款金额"
rules={[
{ required: true, message: '请输入退款金额' },
{ type: 'number', min: 0, max: document.cost, message: `退款金额不能超过¥${document.cost.toFixed(2)}` }
]}
>
<Input type="number" addonAfter="元" />
</Form.Item>
<Form.Item
name="reason"
label="退款原因"
rules={[{ required: true, message: '请输入退款原因' }]}
>
<TextArea rows={3} placeholder="请输入退款原因..." />
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setRefundModalVisible(false)}>
</Button>
<Button type="primary" danger htmlType="submit">
退
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
{/* 添加管理员备注弹窗 */}
<Modal
title="添加管理员备注"
visible={adminNoteModalVisible}
onCancel={() => setAdminNoteModalVisible(false)}
footer={null}
>
<Form
form={noteForm}
layout="vertical"
onFinish={handleAddAdminNote}
initialValues={{ note: document.adminNotes }}
>
<Form.Item
name="note"
label="备注内容"
rules={[{ required: true, message: '请输入备注内容' }]}
>
<TextArea rows={4} placeholder="请输入管理员备注..." />
</Form.Item>
<Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
<Space>
<Button onClick={() => setAdminNoteModalVisible(false)}>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default DocumentDetail;