"use client" import { useEffect, useMemo, useState } from "react" import Link from "next/link" import MobileHeader from "@/app/components/MobileHeader" import BottomNav from "@/app/components/BottomNav" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Card, CardContent } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Label } from "@/components/ui/label" import { Checkbox } from "@/components/ui/checkbox" import { Search, Filter, Plus } from 'lucide-react' import FilterDrawer, { type FilterValues } from "@/components/user-portrait/filter-drawer" type User = { id: string name: string phone: string email: string tags: string[] rfmScore: number lastActivity: string status: "活跃" | "沉睡" | "已封禁" } type UsersResponse = { success: true; data: { items: User[]; total: number; page: number; pageSize: number } } export default function UserPortraitPage() { const [users, setUsers] = useState([]) const [total, setTotal] = useState(0) const [searchQuery, setSearchQuery] = useState("") const [isAddingUser, setIsAddingUser] = useState(false) const [newUser, setNewUser] = useState({ name: "", phone: "", email: "", tags: [] as string[] }) const [filterOpen, setFilterOpen] = useState(false) const [allTags, setAllTags] = useState([]) const [filters, setFilters] = useState({ tags: [], status: [], rfm: [0, 100] }) const queryString = useMemo(() => { const p = new URLSearchParams() if (searchQuery) p.set("q", searchQuery) if (filters.tags.length) p.set("tags", filters.tags.join(",")) if (filters.status.length) p.set("status", filters.status.join(",")) p.set("rfmMin", String(filters.rfm[0])) p.set("rfmMax", String(filters.rfm[1])) p.set("page", "1") p.set("pageSize", "50") return p.toString() }, [searchQuery, filters]) useEffect(() => { fetch(`/api/users?${queryString}`) .then((r) => r.json()) .then((res: UsersResponse) => { if (res?.success) { setUsers(res.data.items) setTotal(res.data.total) } }) .catch(() => {}) }, [queryString]) useEffect(() => { fetch("/api/users?meta=tags") .then((r) => r.json()) .then((res: any) => setAllTags(res?.data?.tags ?? [])) .catch(() => {}) }, []) const handleAddUser = async () => { const resp = await fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newUser), }) const data = await resp.json() if (data?.success) { setIsAddingUser(false) setNewUser({ name: "", phone: "", email: "", tags: [] }) // 触发刷新 fetch(`/api/users?${queryString}`) .then((r) => r.json()) .then((res: UsersResponse) => { if (res?.success) { setUsers(res.data.items) setTotal(res.data.total) } }) } } return (
{}} title="用户画像" />

用户画像

管理与分群

共 {total} 人
用户管理 标签管理
setSearchQuery(e.target.value)} />
{users.map((u) => (
{u.name}

最后活跃 {new Date(u.lastActivity).toLocaleDateString("zh-CN")}

{u.phone}
{u.email}
{u.tags.slice(0, 2).map((t) => ( {t} ))} {u.tags.length > 2 && ( +{u.tags.length - 2} )}
{u.rfmScore}
{u.status}
))}
标签管理将在接入数据字典后提供配置与统计
{/* 筛选抽屉 */} setFilters(v)} /> {/* 添加用户 */} 添加新用户
setNewUser((p) => ({ ...p, name: e.target.value }))} />
setNewUser((p) => ({ ...p, phone: e.target.value }))} />
setNewUser((p) => ({ ...p, email: e.target.value }))} />
{!!allTags.length && (
{allTags.map((t) => ( ))}
)}
) }