refactor: overhaul UI for streamlined user experience
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>
This commit is contained in:
414
app/tag-management/page.tsx
Normal file
414
app/tag-management/page.tsx
Normal file
@@ -0,0 +1,414 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Search, Plus, Filter, Download, Tag, Edit, Trash2, MoreHorizontal } from "lucide-react"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import { TagCategoryChart } from "@/components/tag-management/tag-category-chart"
|
||||
import { TagUsageStats } from "@/components/tag-management/tag-usage-stats"
|
||||
import { TagRelationshipGraph } from "@/components/tag-management/tag-relationship-graph"
|
||||
|
||||
interface TagItem {
|
||||
id: string
|
||||
name: string
|
||||
category: string
|
||||
type: "system" | "custom" | "derived"
|
||||
source: string
|
||||
coverage: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
const mockTags: TagItem[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "高价值用户",
|
||||
category: "用户价值",
|
||||
type: "derived",
|
||||
source: "RFM模型",
|
||||
coverage: 15.3,
|
||||
createdAt: "2023-05-12",
|
||||
updatedAt: "2023-07-15",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "90后",
|
||||
category: "人口属性",
|
||||
type: "system",
|
||||
source: "注册信息",
|
||||
coverage: 42.7,
|
||||
createdAt: "2023-01-05",
|
||||
updatedAt: "2023-01-05",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "游戏爱好者",
|
||||
category: "兴趣爱好",
|
||||
type: "custom",
|
||||
source: "行为分析",
|
||||
coverage: 28.4,
|
||||
createdAt: "2023-04-18",
|
||||
updatedAt: "2023-06-22",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "流失风险高",
|
||||
category: "流失风险",
|
||||
type: "derived",
|
||||
source: "预测模型",
|
||||
coverage: 8.9,
|
||||
createdAt: "2023-06-30",
|
||||
updatedAt: "2023-07-20",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "北京地区",
|
||||
category: "地理位置",
|
||||
type: "system",
|
||||
source: "IP分析",
|
||||
coverage: 12.5,
|
||||
createdAt: "2023-02-15",
|
||||
updatedAt: "2023-02-15",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "周末活跃",
|
||||
category: "活跃时间",
|
||||
type: "custom",
|
||||
source: "行为分析",
|
||||
coverage: 35.2,
|
||||
createdAt: "2023-05-28",
|
||||
updatedAt: "2023-07-10",
|
||||
},
|
||||
]
|
||||
|
||||
const tagCategories = ["用户价值", "人口属性", "兴趣爱好", "流失风险", "地理位置", "活跃时间", "消费能力", "渠道来源"]
|
||||
|
||||
export default function TagManagementPage() {
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([])
|
||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||
const [newTag, setNewTag] = useState({
|
||||
name: "",
|
||||
category: "",
|
||||
type: "custom",
|
||||
source: "",
|
||||
})
|
||||
|
||||
const handleSelectAllTags = (checked: boolean) => {
|
||||
if (checked) {
|
||||
setSelectedTags(mockTags.map((tag) => tag.id))
|
||||
} else {
|
||||
setSelectedTags([])
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectTag = (tagId: string, checked: boolean) => {
|
||||
if (checked) {
|
||||
setSelectedTags([...selectedTags, tagId])
|
||||
} else {
|
||||
setSelectedTags(selectedTags.filter((id) => id !== tagId))
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateTag = () => {
|
||||
// 这里应该是创建标签的逻辑
|
||||
console.log("创建标签:", newTag)
|
||||
setIsCreateDialogOpen(false)
|
||||
setNewTag({
|
||||
name: "",
|
||||
category: "",
|
||||
type: "custom",
|
||||
source: "",
|
||||
})
|
||||
}
|
||||
|
||||
const filteredTags = mockTags.filter(
|
||||
(tag) =>
|
||||
tag.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
tag.category.toLowerCase().includes(searchQuery.toLowerCase()),
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">标签管理</h1>
|
||||
<p className="text-muted-foreground mt-1">管理和组织用户标签体系</p>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="outline">
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
导出标签
|
||||
</Button>
|
||||
<Button onClick={() => setIsCreateDialogOpen(true)}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
创建标签
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium">标签总数</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center">
|
||||
<Tag className="h-5 w-5 text-blue-500 mr-2" />
|
||||
<div className="text-2xl font-bold">128</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">较上月增加 12 个</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium">标签覆盖率</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">87.5%</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">较上月提升 2.3%</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium">标签使用率</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">76.2%</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">较上月提升 5.1%</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="list" className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<TabsList>
|
||||
<TabsTrigger value="list">标签列表</TabsTrigger>
|
||||
<TabsTrigger value="analytics">标签分析</TabsTrigger>
|
||||
<TabsTrigger value="relationships">标签关系</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="flex space-x-2">
|
||||
<div className="relative w-64">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="搜索标签..."
|
||||
className="pl-8"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" size="icon">
|
||||
<Filter className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="list" className="space-y-4">
|
||||
<Card>
|
||||
<CardContent className="p-0">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-12">
|
||||
<Checkbox
|
||||
checked={selectedTags.length === mockTags.length}
|
||||
onCheckedChange={handleSelectAllTags}
|
||||
/>
|
||||
</TableHead>
|
||||
<TableHead>标签名称</TableHead>
|
||||
<TableHead>分类</TableHead>
|
||||
<TableHead>类型</TableHead>
|
||||
<TableHead>数据来源</TableHead>
|
||||
<TableHead>覆盖率</TableHead>
|
||||
<TableHead>创建时间</TableHead>
|
||||
<TableHead>更新时间</TableHead>
|
||||
<TableHead className="text-right">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredTags.map((tag) => (
|
||||
<TableRow key={tag.id}>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selectedTags.includes(tag.id)}
|
||||
onCheckedChange={(checked) => handleSelectTag(tag.id, !!checked)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{tag.name}</TableCell>
|
||||
<TableCell>{tag.category}</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
tag.type === "system"
|
||||
? "bg-blue-50 text-blue-700 border-blue-200"
|
||||
: tag.type === "derived"
|
||||
? "bg-purple-50 text-purple-700 border-purple-200"
|
||||
: "bg-green-50 text-green-700 border-green-200"
|
||||
}
|
||||
>
|
||||
{tag.type === "system" ? "系统" : tag.type === "derived" ? "衍生" : "自定义"}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>{tag.source}</TableCell>
|
||||
<TableCell>{tag.coverage}%</TableCell>
|
||||
<TableCell>{tag.createdAt}</TableCell>
|
||||
<TableCell>{tag.updatedAt}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
编辑
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
删除
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="analytics" className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>标签分类分布</CardTitle>
|
||||
<CardDescription>按分类统计标签数量</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<TagCategoryChart />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>标签使用统计</CardTitle>
|
||||
<CardDescription>标签在各系统中的使用情况</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<TagUsageStats />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="relationships" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>标签关系图谱</CardTitle>
|
||||
<CardDescription>展示标签之间的关联关系</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="h-[500px]">
|
||||
<TagRelationshipGraph />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* 创建标签对话框 */}
|
||||
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>创建新标签</DialogTitle>
|
||||
<DialogDescription>添加新的用户标签到标签体系中</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="tag-name" className="text-right">
|
||||
标签名称
|
||||
</Label>
|
||||
<Input
|
||||
id="tag-name"
|
||||
value={newTag.name}
|
||||
onChange={(e) => setNewTag({ ...newTag, name: e.target.value })}
|
||||
className="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="tag-category" className="text-right">
|
||||
标签分类
|
||||
</Label>
|
||||
<Select value={newTag.category} onValueChange={(value) => setNewTag({ ...newTag, category: value })}>
|
||||
<SelectTrigger className="col-span-3">
|
||||
<SelectValue placeholder="选择标签分类" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{tagCategories.map((category) => (
|
||||
<SelectItem key={category} value={category}>
|
||||
{category}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="tag-type" className="text-right">
|
||||
标签类型
|
||||
</Label>
|
||||
<Select
|
||||
value={newTag.type}
|
||||
onValueChange={(value: "system" | "custom" | "derived") => setNewTag({ ...newTag, type: value })}
|
||||
>
|
||||
<SelectTrigger className="col-span-3">
|
||||
<SelectValue placeholder="选择标签类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="system">系统标签</SelectItem>
|
||||
<SelectItem value="custom">自定义标签</SelectItem>
|
||||
<SelectItem value="derived">衍生标签</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="tag-source" className="text-right">
|
||||
数据来源
|
||||
</Label>
|
||||
<Input
|
||||
id="tag-source"
|
||||
value={newTag.source}
|
||||
onChange={(e) => setNewTag({ ...newTag, source: e.target.value })}
|
||||
className="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleCreateTag}>创建</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user