Redesign navigation, home overview, user portrait, and valuation pages with improved functionality and responsive design. Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
241 lines
7.7 KiB
TypeScript
241 lines
7.7 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|
import { Search, FileDown, Eye, RefreshCw } from "lucide-react"
|
|
import {
|
|
Pagination,
|
|
PaginationContent,
|
|
PaginationItem,
|
|
PaginationLink,
|
|
PaginationNext,
|
|
PaginationPrevious,
|
|
} from "@/components/ui/pagination"
|
|
|
|
interface IntegrationRecord {
|
|
id: string
|
|
sourceId: string
|
|
sourceName: string
|
|
startTime: Date
|
|
endTime: Date
|
|
status: "success" | "failed" | "partial" | "running"
|
|
totalRecords: number
|
|
successRecords: number
|
|
failedRecords: number
|
|
notes: string
|
|
}
|
|
|
|
export function IntegrationHistory() {
|
|
const [searchQuery, setSearchQuery] = useState("")
|
|
const [statusFilter, setStatusFilter] = useState("all")
|
|
|
|
// 模拟数据
|
|
const integrationRecords: IntegrationRecord[] = [
|
|
{
|
|
id: "1",
|
|
sourceId: "1",
|
|
sourceName: "CRM系统用户数据",
|
|
startTime: new Date(2023, 6, 15, 14, 30),
|
|
endTime: new Date(2023, 6, 15, 14, 45),
|
|
status: "success",
|
|
totalRecords: 5280,
|
|
successRecords: 5280,
|
|
failedRecords: 0,
|
|
notes: "完全同步成功",
|
|
},
|
|
{
|
|
id: "2",
|
|
sourceId: "2",
|
|
sourceName: "电商平台订单数据",
|
|
startTime: new Date(2023, 6, 15, 10, 15),
|
|
endTime: new Date(2023, 6, 15, 10, 40),
|
|
status: "partial",
|
|
totalRecords: 12500,
|
|
successRecords: 12350,
|
|
failedRecords: 150,
|
|
notes: "部分记录格式错误",
|
|
},
|
|
{
|
|
id: "3",
|
|
sourceId: "3",
|
|
sourceName: "营销活动参与用户",
|
|
startTime: new Date(2023, 6, 14, 9, 45),
|
|
endTime: new Date(2023, 6, 14, 9, 50),
|
|
status: "success",
|
|
totalRecords: 875,
|
|
successRecords: 875,
|
|
failedRecords: 0,
|
|
notes: "同步成功",
|
|
},
|
|
{
|
|
id: "4",
|
|
sourceId: "4",
|
|
sourceName: "APP用户行为数据",
|
|
startTime: new Date(2023, 6, 10, 16, 20),
|
|
endTime: new Date(2023, 6, 10, 16, 35),
|
|
status: "failed",
|
|
totalRecords: 8500,
|
|
successRecords: 0,
|
|
failedRecords: 8500,
|
|
notes: "API连接超时",
|
|
},
|
|
{
|
|
id: "5",
|
|
sourceId: "5",
|
|
sourceName: "社交媒体用户数据",
|
|
startTime: new Date(2023, 6, 15, 8, 0),
|
|
endTime: new Date(2023, 6, 15, 8, 15),
|
|
status: "success",
|
|
totalRecords: 3200,
|
|
successRecords: 3200,
|
|
failedRecords: 0,
|
|
notes: "同步成功",
|
|
},
|
|
{
|
|
id: "6",
|
|
sourceId: "7",
|
|
sourceName: "新用户注册数据",
|
|
startTime: new Date(2023, 6, 15, 16, 0),
|
|
endTime: null,
|
|
status: "running",
|
|
totalRecords: 0,
|
|
successRecords: 0,
|
|
failedRecords: 0,
|
|
notes: "首次同步进行中",
|
|
},
|
|
]
|
|
|
|
// 过滤记录
|
|
const filteredRecords = integrationRecords.filter((record) => {
|
|
const matchesSearch = record.sourceName.toLowerCase().includes(searchQuery.toLowerCase())
|
|
const matchesStatus = statusFilter === "all" || record.status === statusFilter
|
|
return matchesSearch && matchesStatus
|
|
})
|
|
|
|
const formatDate = (date: Date | null) => {
|
|
if (!date) return "进行中"
|
|
return date.toLocaleString("zh-CN", {
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
})
|
|
}
|
|
|
|
const getStatusBadge = (status: IntegrationRecord["status"]) => {
|
|
switch (status) {
|
|
case "success":
|
|
return <Badge className="bg-green-100 text-green-800">成功</Badge>
|
|
case "failed":
|
|
return <Badge className="bg-red-100 text-red-800">失败</Badge>
|
|
case "partial":
|
|
return <Badge className="bg-yellow-100 text-yellow-800">部分成功</Badge>
|
|
case "running":
|
|
return <Badge className="bg-blue-100 text-blue-800">进行中</Badge>
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex space-x-2">
|
|
<div className="relative w-64">
|
|
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
<Input
|
|
placeholder="搜索数据源..."
|
|
className="pl-8"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
|
<SelectTrigger className="w-[150px]">
|
|
<SelectValue placeholder="状态筛选" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="all">全部状态</SelectItem>
|
|
<SelectItem value="success">成功</SelectItem>
|
|
<SelectItem value="failed">失败</SelectItem>
|
|
<SelectItem value="partial">部分成功</SelectItem>
|
|
<SelectItem value="running">进行中</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<Button variant="outline">
|
|
<RefreshCw className="mr-2 h-4 w-4" />
|
|
刷新
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="rounded-md border">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>数据源</TableHead>
|
|
<TableHead>开始时间</TableHead>
|
|
<TableHead>结束时间</TableHead>
|
|
<TableHead>状态</TableHead>
|
|
<TableHead>总记录数</TableHead>
|
|
<TableHead>成功数</TableHead>
|
|
<TableHead>失败数</TableHead>
|
|
<TableHead>备注</TableHead>
|
|
<TableHead className="text-right">操作</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filteredRecords.map((record) => (
|
|
<TableRow key={record.id}>
|
|
<TableCell className="font-medium">{record.sourceName}</TableCell>
|
|
<TableCell>{formatDate(record.startTime)}</TableCell>
|
|
<TableCell>{formatDate(record.endTime)}</TableCell>
|
|
<TableCell>{getStatusBadge(record.status)}</TableCell>
|
|
<TableCell>{record.totalRecords.toLocaleString()}</TableCell>
|
|
<TableCell>{record.successRecords.toLocaleString()}</TableCell>
|
|
<TableCell>{record.failedRecords.toLocaleString()}</TableCell>
|
|
<TableCell>{record.notes}</TableCell>
|
|
<TableCell className="text-right">
|
|
<div className="flex justify-end space-x-2">
|
|
<Button variant="ghost" size="icon">
|
|
<Eye className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="ghost" size="icon">
|
|
<FileDown className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
<Pagination>
|
|
<PaginationContent>
|
|
<PaginationItem>
|
|
<PaginationPrevious href="#" />
|
|
</PaginationItem>
|
|
<PaginationItem>
|
|
<PaginationLink href="#">1</PaginationLink>
|
|
</PaginationItem>
|
|
<PaginationItem>
|
|
<PaginationLink href="#" isActive>
|
|
2
|
|
</PaginationLink>
|
|
</PaginationItem>
|
|
<PaginationItem>
|
|
<PaginationLink href="#">3</PaginationLink>
|
|
</PaginationItem>
|
|
<PaginationItem>
|
|
<PaginationNext href="#" />
|
|
</PaginationItem>
|
|
</PaginationContent>
|
|
</Pagination>
|
|
</div>
|
|
)
|
|
}
|