- 修复消息组件JSX.Element类型错误,改为React.ReactNode - 完善审核历史页面类型定义和API接口调用 - 优化验证码组件,移除备用验证码逻辑避免无限循环 - 简化系统设置页面,仅保留基本设置和外观设置 - 修复用户管理页面编辑模态框数据加载和CRUD操作 - 移除废弃的作物推荐组件文件 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Label } from '@/components/ui/label';
|
||
import { Card } from '@/components/ui/card';
|
||
import { Badge } from '@/components/ui/badge';
|
||
import { format } from 'date-fns';
|
||
import { zhCN } from 'date-fns/locale';
|
||
import { MessageSendRecord } from '@/types/message';
|
||
|
||
interface MessagePreviewDialogProps {
|
||
open: boolean;
|
||
onOpenChange: (open: boolean) => void;
|
||
record: MessageSendRecord | null;
|
||
getTypeIcon: (type: string) => React.ReactNode;
|
||
getTypeLabel: (type: string) => string;
|
||
getTypeBadge: (type: string) => string;
|
||
getStatusBadge: (status: string) => React.ReactNode;
|
||
}
|
||
|
||
export function MessagePreviewDialog({
|
||
open,
|
||
onOpenChange,
|
||
record,
|
||
getTypeIcon,
|
||
getTypeLabel,
|
||
getTypeBadge,
|
||
getStatusBadge
|
||
}: MessagePreviewDialogProps) {
|
||
if (!record) return null;
|
||
|
||
return (
|
||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||
<DialogContent className="max-w-2xl">
|
||
<DialogHeader>
|
||
<DialogTitle>消息详情</DialogTitle>
|
||
<DialogDescription className="sr-only">
|
||
查看消息发送详情
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
<div className="space-y-4">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<Label>消息模版</Label>
|
||
<div className="field-value-inline">{record.templateName}</div>
|
||
</div>
|
||
<div>
|
||
<Label>消息类型</Label>
|
||
<div className="mt-2">
|
||
<Badge className={getTypeBadge(record.type)}>
|
||
<span className="flex items-center gap-1">
|
||
{getTypeIcon(record.type)}
|
||
{getTypeLabel(record.type)}
|
||
</span>
|
||
</Badge>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<Label>发送方式</Label>
|
||
<div className="field-value-inline">
|
||
{record.sendType === 'immediate' ? '实时发送' : '定时发送'}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<Label>发送状态</Label>
|
||
<div className="mt-2">
|
||
{getStatusBadge(record.status)}
|
||
</div>
|
||
</div>
|
||
{record.scheduledTime && (
|
||
<div>
|
||
<Label>定时发送时间</Label>
|
||
<div className="field-value-inline">
|
||
{format(new Date(record.scheduledTime), 'yyyy-MM-dd HH:mm', { locale: zhCN })}
|
||
</div>
|
||
</div>
|
||
)}
|
||
<div>
|
||
<Label>创建时间</Label>
|
||
<div className="field-value-inline">
|
||
{format(new Date(record.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN })}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{record.subject && (
|
||
<div>
|
||
<Label>消息主题</Label>
|
||
<div className="field-value-inline">{record.subject}</div>
|
||
</div>
|
||
)}
|
||
|
||
<div>
|
||
<Label>接收人列表(共 {record.recipientCount} 人)</Label>
|
||
<Card className="p-3 bg-gray-50 mt-2">
|
||
<div className="flex flex-wrap gap-2">
|
||
{record.recipients.map((recipient, index) => (
|
||
<Badge key={index} variant="outline">
|
||
{recipient}
|
||
</Badge>
|
||
))}
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
|
||
<div>
|
||
<Label>消息内容</Label>
|
||
<Card className="p-4 bg-blue-50 border-blue-200 mt-2">
|
||
<pre className="text-sm whitespace-pre-wrap">
|
||
{record.content}
|
||
</pre>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
<DialogFooter>
|
||
<Button onClick={() => onOpenChange(false)}>
|
||
关闭
|
||
</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
);
|
||
} |