322 lines
12 KiB
TypeScript
322 lines
12 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 { Input } from "@/components/ui/input"
|
|||
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|||
|
|
import { Switch } from "@/components/ui/switch"
|
|||
|
|
import { Badge } from "@/components/ui/badge"
|
|||
|
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
|||
|
|
import { ArrowRight, Plus, Save, Trash2, HelpCircle } from "lucide-react"
|
|||
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
|||
|
|
|
|||
|
|
interface FieldMapping {
|
|||
|
|
id: string
|
|||
|
|
sourceField: string
|
|||
|
|
targetField: string
|
|||
|
|
dataType: string
|
|||
|
|
required: boolean
|
|||
|
|
transformation: string
|
|||
|
|
primaryKey: boolean
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function DataFieldMapping() {
|
|||
|
|
// 模拟数据
|
|||
|
|
const [mappings, setMappings] = useState<FieldMapping[]>([
|
|||
|
|
{
|
|||
|
|
id: "1",
|
|||
|
|
sourceField: "user_id",
|
|||
|
|
targetField: "userId",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: true,
|
|||
|
|
transformation: "none",
|
|||
|
|
primaryKey: true,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "2",
|
|||
|
|
sourceField: "phone",
|
|||
|
|
targetField: "phoneNumber",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: true,
|
|||
|
|
transformation: "formatPhoneNumber",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "3",
|
|||
|
|
sourceField: "id_card",
|
|||
|
|
targetField: "identityNumber",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: true,
|
|||
|
|
transformation: "mask",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "4",
|
|||
|
|
sourceField: "imei",
|
|||
|
|
targetField: "deviceImei",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: false,
|
|||
|
|
transformation: "none",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "5",
|
|||
|
|
sourceField: "name",
|
|||
|
|
targetField: "fullName",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: true,
|
|||
|
|
transformation: "none",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "6",
|
|||
|
|
sourceField: "register_time",
|
|||
|
|
targetField: "registrationDate",
|
|||
|
|
dataType: "datetime",
|
|||
|
|
required: true,
|
|||
|
|
transformation: "parseDateTime",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "7",
|
|||
|
|
sourceField: "last_login",
|
|||
|
|
targetField: "lastActiveTime",
|
|||
|
|
dataType: "datetime",
|
|||
|
|
required: false,
|
|||
|
|
transformation: "parseDateTime",
|
|||
|
|
primaryKey: false,
|
|||
|
|
},
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
const addNewMapping = () => {
|
|||
|
|
const newMapping: FieldMapping = {
|
|||
|
|
id: `new-${Date.now()}`,
|
|||
|
|
sourceField: "",
|
|||
|
|
targetField: "",
|
|||
|
|
dataType: "string",
|
|||
|
|
required: false,
|
|||
|
|
transformation: "none",
|
|||
|
|
primaryKey: false,
|
|||
|
|
}
|
|||
|
|
setMappings([...mappings, newMapping])
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const updateMapping = (id: string, field: keyof FieldMapping, value: any) => {
|
|||
|
|
setMappings(mappings.map((mapping) => (mapping.id === id ? { ...mapping, [field]: value } : mapping)))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const deleteMapping = (id: string) => {
|
|||
|
|
setMappings(mappings.filter((mapping) => mapping.id !== id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="space-y-4">
|
|||
|
|
<Card>
|
|||
|
|
<CardHeader>
|
|||
|
|
<CardTitle>数据字段映射</CardTitle>
|
|||
|
|
<CardDescription>
|
|||
|
|
配置源数据字段与目标数据字段的映射关系,以ID、手机号、身份证号和IMEI设备号为标准整合客户数据
|
|||
|
|
</CardDescription>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent className="space-y-4">
|
|||
|
|
<div className="flex justify-between items-center">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<Badge className="bg-blue-100 text-blue-800">标准字段映射</Badge>
|
|||
|
|
<TooltipProvider>
|
|||
|
|
<Tooltip>
|
|||
|
|
<TooltipTrigger asChild>
|
|||
|
|
<Button variant="ghost" size="icon">
|
|||
|
|
<HelpCircle className="h-4 w-4" />
|
|||
|
|
</Button>
|
|||
|
|
</TooltipTrigger>
|
|||
|
|
<TooltipContent>
|
|||
|
|
<p className="max-w-xs">
|
|||
|
|
标准字段映射用于将不同数据源的字段映射到统一的标准字段,以便于数据整合和分析。
|
|||
|
|
主键字段用于唯一标识用户,通常为用户ID、手机号、身份证号或IMEI设备号。
|
|||
|
|
</p>
|
|||
|
|
</TooltipContent>
|
|||
|
|
</Tooltip>
|
|||
|
|
</TooltipProvider>
|
|||
|
|
</div>
|
|||
|
|
<Button onClick={addNewMapping}>
|
|||
|
|
<Plus 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 className="text-right">操作</TableHead>
|
|||
|
|
</TableRow>
|
|||
|
|
</TableHeader>
|
|||
|
|
<TableBody>
|
|||
|
|
{mappings.map((mapping) => (
|
|||
|
|
<TableRow key={mapping.id}>
|
|||
|
|
<TableCell>
|
|||
|
|
<Input
|
|||
|
|
value={mapping.sourceField}
|
|||
|
|
onChange={(e) => updateMapping(mapping.id, "sourceField", e.target.value)}
|
|||
|
|
placeholder="源字段名"
|
|||
|
|
/>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<ArrowRight className="h-4 w-4 text-muted-foreground" />
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Input
|
|||
|
|
value={mapping.targetField}
|
|||
|
|
onChange={(e) => updateMapping(mapping.id, "targetField", e.target.value)}
|
|||
|
|
placeholder="目标字段名"
|
|||
|
|
/>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Select
|
|||
|
|
value={mapping.dataType}
|
|||
|
|
onValueChange={(value) => updateMapping(mapping.id, "dataType", value)}
|
|||
|
|
>
|
|||
|
|
<SelectTrigger className="w-full">
|
|||
|
|
<SelectValue placeholder="选择数据类型" />
|
|||
|
|
</SelectTrigger>
|
|||
|
|
<SelectContent>
|
|||
|
|
<SelectItem value="string">字符串</SelectItem>
|
|||
|
|
<SelectItem value="number">数字</SelectItem>
|
|||
|
|
<SelectItem value="boolean">布尔值</SelectItem>
|
|||
|
|
<SelectItem value="datetime">日期时间</SelectItem>
|
|||
|
|
<SelectItem value="array">数组</SelectItem>
|
|||
|
|
<SelectItem value="object">对象</SelectItem>
|
|||
|
|
</SelectContent>
|
|||
|
|
</Select>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<div className="flex items-center justify-center">
|
|||
|
|
<Switch
|
|||
|
|
checked={mapping.required}
|
|||
|
|
onCheckedChange={(checked) => updateMapping(mapping.id, "required", checked)}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<div className="flex items-center justify-center">
|
|||
|
|
<Switch
|
|||
|
|
checked={mapping.primaryKey}
|
|||
|
|
onCheckedChange={(checked) => updateMapping(mapping.id, "primaryKey", checked)}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Select
|
|||
|
|
value={mapping.transformation}
|
|||
|
|
onValueChange={(value) => updateMapping(mapping.id, "transformation", value)}
|
|||
|
|
>
|
|||
|
|
<SelectTrigger className="w-full">
|
|||
|
|
<SelectValue placeholder="选择转换方式" />
|
|||
|
|
</SelectTrigger>
|
|||
|
|
<SelectContent>
|
|||
|
|
<SelectItem value="none">无转换</SelectItem>
|
|||
|
|
<SelectItem value="formatPhoneNumber">格式化手机号</SelectItem>
|
|||
|
|
<SelectItem value="mask">数据脱敏</SelectItem>
|
|||
|
|
<SelectItem value="parseDateTime">解析日期时间</SelectItem>
|
|||
|
|
<SelectItem value="uppercase">转大写</SelectItem>
|
|||
|
|
<SelectItem value="lowercase">转小写</SelectItem>
|
|||
|
|
<SelectItem value="trim">去除空格</SelectItem>
|
|||
|
|
<SelectItem value="custom">自定义转换</SelectItem>
|
|||
|
|
</SelectContent>
|
|||
|
|
</Select>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell className="text-right">
|
|||
|
|
<Button variant="ghost" size="icon" onClick={() => deleteMapping(mapping.id)}>
|
|||
|
|
<Trash2 className="h-4 w-4 text-red-500" />
|
|||
|
|
</Button>
|
|||
|
|
</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
))}
|
|||
|
|
</TableBody>
|
|||
|
|
</Table>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="flex justify-end space-x-2">
|
|||
|
|
<Button variant="outline">取消</Button>
|
|||
|
|
<Button>
|
|||
|
|
<Save className="mr-2 h-4 w-4" />
|
|||
|
|
保存配置
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
|
|||
|
|
<Card>
|
|||
|
|
<CardHeader>
|
|||
|
|
<CardTitle>标准字段说明</CardTitle>
|
|||
|
|
<CardDescription>用户数据标准字段的说明和用途</CardDescription>
|
|||
|
|
</CardHeader>
|
|||
|
|
<CardContent>
|
|||
|
|
<Table>
|
|||
|
|
<TableHeader>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableHead>字段名</TableHead>
|
|||
|
|
<TableHead>数据类型</TableHead>
|
|||
|
|
<TableHead>说明</TableHead>
|
|||
|
|
<TableHead>用途</TableHead>
|
|||
|
|
</TableRow>
|
|||
|
|
</TableHeader>
|
|||
|
|
<TableBody>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">userId</TableCell>
|
|||
|
|
<TableCell>字符串</TableCell>
|
|||
|
|
<TableCell>用户唯一标识符</TableCell>
|
|||
|
|
<TableCell>用于唯一标识用户,主键字段</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">phoneNumber</TableCell>
|
|||
|
|
<TableCell>字符串</TableCell>
|
|||
|
|
<TableCell>用户手机号码</TableCell>
|
|||
|
|
<TableCell>用于用户联系和身份验证,可作为主键</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">identityNumber</TableCell>
|
|||
|
|
<TableCell>字符串</TableCell>
|
|||
|
|
<TableCell>用户身份证号码</TableCell>
|
|||
|
|
<TableCell>用于用户实名认证,可作为主键</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">deviceImei</TableCell>
|
|||
|
|
<TableCell>字符串</TableCell>
|
|||
|
|
<TableCell>设备IMEI号码</TableCell>
|
|||
|
|
<TableCell>用于设备识别,可作为主键</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">fullName</TableCell>
|
|||
|
|
<TableCell>字符串</TableCell>
|
|||
|
|
<TableCell>用户姓名</TableCell>
|
|||
|
|
<TableCell>用于用户基本信息</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">registrationDate</TableCell>
|
|||
|
|
<TableCell>日期时间</TableCell>
|
|||
|
|
<TableCell>用户注册时间</TableCell>
|
|||
|
|
<TableCell>用于用户生命周期分析</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell className="font-medium">lastActiveTime</TableCell>
|
|||
|
|
<TableCell>日期时间</TableCell>
|
|||
|
|
<TableCell>用户最后活跃时间</TableCell>
|
|||
|
|
<TableCell>用于用户活跃度分析</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
</TableBody>
|
|||
|
|
</Table>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|