feat: enhance user profile with detailed tags and asset evaluation

Optimize user detail page for asset assessment and tag info.

#VERCEL_SKIP

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2025-08-21 05:32:37 +00:00
parent 9bb0ee2758
commit afc77439bb
25 changed files with 2421 additions and 1645 deletions

View File

@@ -1,6 +1,6 @@
import { NextResponse, NextRequest } from "next/server"
import { NextResponse } from "next/server"
import type { TrafficUser } from "@/types/traffic"
import { addUser, filterUsers, getDistinctTags, getUserById, queryUsers, type UserStatus, MOCK_USERS } from "@/lib/mock-users"
import { addUser, getUsersStore, queryUsers, type User } from "@/lib/mock-users"
// 中文名字生成器数据
const familyNames = [
@@ -127,6 +127,9 @@ const userPool: TrafficUser[] = Array.from({ length: 1610 }, (_, i) => {
assignedTo: "",
category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"],
tags: [],
city: "",
persona: "",
rfmScore: Math.floor(Math.random() * 101),
}
})
@@ -185,6 +188,9 @@ const generateWechatFriends = (wechatId: string, count: number) => {
assignedTo: "",
category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"],
tags: [],
city: "",
persona: "",
rfmScore: Math.floor(Math.random() * 101),
}
})
}
@@ -194,33 +200,57 @@ const wechatFriendsCache = new Map<string, TrafficUser[]>()
function parseArrayParam(v: string | null) {
if (!v) return []
return v.split(",").map((s) => s.trim()).filter(Boolean)
return v
.split(",")
.map((s) => s.trim())
.filter(Boolean)
}
export const dynamic = "force-dynamic"
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const keyword = (searchParams.get("q") || "").trim()
export async function GET(req: Request) {
const url = new URL(req.url)
const meta = url.searchParams.get("meta")
if (meta === "tags") {
const tags = Array.from(new Set(getUsersStore().flatMap((u) => u.tags))).sort()
const cities = Array.from(new Set(getUsersStore().map((u) => u.city))).sort()
const personas = Array.from(new Set(getUsersStore().flatMap((u) => u.persona))).sort()
const sources = Array.from(new Set(getUsersStore().map((u) => u.source))).sort()
return NextResponse.json({ success: true, data: { tags, cities, personas, sources } })
}
const filtered = keyword
? MOCK_USERS.filter(
(u) =>
u.name.includes(keyword) ||
(u.nickname && u.nickname.includes(keyword)) ||
u.tags.some((t) => t.includes(keyword)),
)
: MOCK_USERS
const q = url.searchParams.get("q") ?? undefined
const tags = url.searchParams.get("tags")?.split(",").filter(Boolean)
const status = url.searchParams.get("status")?.split(",").filter(Boolean) as any
const city = url.searchParams.get("city")?.split(",").filter(Boolean)
const persona = url.searchParams.get("persona")?.split(",").filter(Boolean)
const source = url.searchParams.get("source")?.split(",").filter(Boolean)
const rfmMin = Number(url.searchParams.get("rfmMin") ?? 0)
const rfmMax = Number(url.searchParams.get("rfmMax") ?? 100)
const page = Number(url.searchParams.get("page") ?? 1)
const pageSize = Number(url.searchParams.get("pageSize") ?? 20)
return NextResponse.json({
success: true,
total: filtered.length,
items: filtered,
const { data, pagination } = queryUsers({
q,
tags,
status,
city,
persona,
source,
rfmMin,
rfmMax,
page,
pageSize,
})
// 用户估值:简单以 rfmScore * 100 作为估值
const totalValue = data.reduce((sum, u) => sum + u.rfmScore * 100, 0)
return NextResponse.json({ success: true, data: { items: data, pagination, totalValue } })
}
export async function POST(req: NextRequest) {
const body = await req.json().catch(() => ({}))
const created = addUser(body ?? {})
return NextResponse.json({ data: created }, { status: 201 })
export async function POST(req: Request) {
const body = (await req.json()) as Partial<User>
const u = addUser(body)
return NextResponse.json({ success: true, data: u })
}