diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/api/devices.ts b/api/devices.ts new file mode 100644 index 0000000..347269f --- /dev/null +++ b/api/devices.ts @@ -0,0 +1,130 @@ +import type { + ApiResponse, + Device, + DeviceStats, + DeviceTaskRecord, + PaginatedResponse, + QueryDeviceParams, + CreateDeviceParams, + UpdateDeviceParams, + DeviceStatus, // Added DeviceStatus import +} from "@/types/device" + +const API_BASE = "/api/devices" + +// 设备管理API +export const deviceApi = { + // 创建设备 + async create(params: CreateDeviceParams): Promise> { + const response = await fetch(`${API_BASE}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }) + return response.json() + }, + + // 更新设备 + async update(params: UpdateDeviceParams): Promise> { + const response = await fetch(`${API_BASE}/${params.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }) + return response.json() + }, + + // 获取设备详情 + async getById(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}`) + return response.json() + }, + + // 查询设备列表 + async query(params: QueryDeviceParams): Promise>> { + const queryString = new URLSearchParams({ + ...params, + tags: params.tags ? JSON.stringify(params.tags) : "", + dateRange: params.dateRange ? JSON.stringify(params.dateRange) : "", + }).toString() + + const response = await fetch(`${API_BASE}?${queryString}`) + return response.json() + }, + + // 删除设备 + async delete(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}`, { + method: "DELETE", + }) + return response.json() + }, + + // 重启设备 + async restart(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/restart`, { + method: "POST", + }) + return response.json() + }, + + // 解绑设备 + async unbind(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/unbind`, { + method: "POST", + }) + return response.json() + }, + + // 获取设备统计数据 + async getStats(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/stats`) + return response.json() + }, + + // 获取设备任务记录 + async getTaskRecords(id: string, page = 1, pageSize = 20): Promise>> { + const response = await fetch(`${API_BASE}/${id}/tasks?page=${page}&pageSize=${pageSize}`) + return response.json() + }, + + // 批量更新设备标签 + async updateTags(ids: string[], tags: string[]): Promise> { + const response = await fetch(`${API_BASE}/tags`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ deviceIds: ids, tags }), + }) + return response.json() + }, + + // 批量导出设备数据 + async exportDevices(ids: string[]): Promise { + const response = await fetch(`${API_BASE}/export`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ deviceIds: ids }), + }) + return response.blob() + }, + + // 检查设备在线状态 + async checkStatus(ids: string[]): Promise>> { + const response = await fetch(`${API_BASE}/status`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ deviceIds: ids }), + }) + return response.json() + }, +} diff --git a/api/route.ts b/api/route.ts new file mode 100644 index 0000000..7b5e7c0 --- /dev/null +++ b/api/route.ts @@ -0,0 +1,73 @@ +import { NextResponse } from "next/server" +import type { CreateScenarioParams, QueryScenarioParams, ScenarioBase, ApiResponse } from "@/types/scenario" + +// 获客场景路由处理 +export async function POST(request: Request) { + try { + const body: CreateScenarioParams = await request.json() + + // TODO: 实现创建场景的具体逻辑 + const scenario: ScenarioBase = { + id: "generated-id", + ...body, + status: "draft", + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + creator: "current-user-id", + } + + const response: ApiResponse = { + code: 0, + message: "创建成功", + data: scenario, + } + + return NextResponse.json(response) + } catch (error) { + return NextResponse.json( + { + code: 500, + message: "创建失败", + data: null, + }, + { status: 500 }, + ) + } +} + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url) + const params: QueryScenarioParams = { + type: searchParams.get("type") as any, + status: searchParams.get("status") as any, + keyword: searchParams.get("keyword") || undefined, + dateRange: searchParams.get("dateRange") ? JSON.parse(searchParams.get("dateRange")!) : undefined, + page: Number(searchParams.get("page")) || 1, + pageSize: Number(searchParams.get("pageSize")) || 20, + } + + // TODO: 实现查询场景列表的具体逻辑 + + return NextResponse.json({ + code: 0, + message: "查询成功", + data: { + items: [], + total: 0, + page: params.page, + pageSize: params.pageSize, + totalPages: 0, + }, + }) + } catch (error) { + return NextResponse.json( + { + code: 500, + message: "查询失败", + data: null, + }, + { status: 500 }, + ) + } +} diff --git a/api/scenarios.ts b/api/scenarios.ts new file mode 100644 index 0000000..37786f6 --- /dev/null +++ b/api/scenarios.ts @@ -0,0 +1,111 @@ +import type { + ApiResponse, + CreateScenarioParams, + UpdateScenarioParams, + QueryScenarioParams, + ScenarioBase, + ScenarioStats, + AcquisitionRecord, + PaginatedResponse, +} from "@/types/scenario" + +const API_BASE = "/api/scenarios" + +// 获客场景API +export const scenarioApi = { + // 创建场景 + async create(params: CreateScenarioParams): Promise> { + const response = await fetch(`${API_BASE}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }) + return response.json() + }, + + // 更新场景 + async update(params: UpdateScenarioParams): Promise> { + const response = await fetch(`${API_BASE}/${params.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(params), + }) + return response.json() + }, + + // 获取场景详情 + async getById(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}`) + return response.json() + }, + + // 查询场景列表 + async query(params: QueryScenarioParams): Promise>> { + const queryString = new URLSearchParams({ + ...params, + dateRange: params.dateRange ? JSON.stringify(params.dateRange) : "", + }).toString() + + const response = await fetch(`${API_BASE}?${queryString}`) + return response.json() + }, + + // 删除场景 + async delete(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}`, { + method: "DELETE", + }) + return response.json() + }, + + // 启动场景 + async start(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/start`, { + method: "POST", + }) + return response.json() + }, + + // 暂停场景 + async pause(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/pause`, { + method: "POST", + }) + return response.json() + }, + + // 获取场景统计数据 + async getStats(id: string): Promise> { + const response = await fetch(`${API_BASE}/${id}/stats`) + return response.json() + }, + + // 获取获客记录 + async getRecords(id: string, page = 1, pageSize = 20): Promise>> { + const response = await fetch(`${API_BASE}/${id}/records?page=${page}&pageSize=${pageSize}`) + return response.json() + }, + + // 导出获客记录 + async exportRecords(id: string, dateRange?: { start: string; end: string }): Promise { + const queryString = dateRange ? `?start=${dateRange.start}&end=${dateRange.end}` : "" + const response = await fetch(`${API_BASE}/${id}/records/export${queryString}`) + return response.blob() + }, + + // 批量更新标签 + async updateTags(id: string, customerIds: string[], tags: string[]): Promise> { + const response = await fetch(`${API_BASE}/${id}/tags`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ customerIds, tags }), + }) + return response.json() + }, +} diff --git a/app/ClientLayout.tsx b/app/ClientLayout.tsx new file mode 100644 index 0000000..c4011a8 --- /dev/null +++ b/app/ClientLayout.tsx @@ -0,0 +1,71 @@ +"use client" + +import type React from "react" +import "./globals.css" +import { Inter } from "next/font/google" +import { useState, useEffect } from "react" +import Sidebar from "./components/Sidebar" +import MobileHeader from "./components/MobileHeader" +import MobileSidebar from "./components/MobileSidebar" +import BottomNav from "./components/BottomNav" + +const inter = Inter({ subsets: ["latin"] }) + +export default function ClientLayout({ + children, +}: { + children: React.ReactNode +}) { + const [isMobile, setIsMobile] = useState(false) + const [sidebarOpen, setSidebarOpen] = useState(false) + + useEffect(() => { + const checkMobile = () => { + setIsMobile(window.innerWidth < 768) + } + + checkMobile() + window.addEventListener("resize", checkMobile) + + return () => window.removeEventListener("resize", checkMobile) + }, []) + + return ( + + + 用户数据资产中台 + + + + + {/* 背景装饰 */} +
+
+
+
+
+
+ +
+ {/* 桌面端侧边栏 */} + {!isMobile && } + + {/* 移动端侧边栏 */} + {isMobile && setSidebarOpen(false)} />} + + {/* 主内容区域 */} +
+ {/* 移动端头部 */} + {isMobile && setSidebarOpen(true)} />} + + {/* 内容区域 */} +
{children}
+
+
+ + {/* 移动端底部导航 */} + {isMobile && } + + + ) +} diff --git a/app/ai-assistant/page.tsx b/app/ai-assistant/page.tsx new file mode 100644 index 0000000..f1f6be6 --- /dev/null +++ b/app/ai-assistant/page.tsx @@ -0,0 +1,786 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Textarea } from "@/components/ui/textarea" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Switch } from "@/components/ui/switch" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Sparkles, BarChart, TrendingUp, Users, Mail, Clock, Download, ChevronRight } from "lucide-react" + +export default function AIAssistantPage() { + const [activeTab, setActiveTab] = useState("data-analysis") + const [selectedUserGroup, setSelectedUserGroup] = useState("") + const [isUserGroupDialogOpen, setIsUserGroupDialogOpen] = useState(false) + const [emailAddress, setEmailAddress] = useState("") + const [scheduleReport, setScheduleReport] = useState(false) + + // 分析流程步骤 + const [analysisStep, setAnalysisStep] = useState(1) + const [analysisType, setAnalysisType] = useState("") + + // 营销策略步骤 + const [strategyStep, setStrategyStep] = useState(1) + const [strategyType, setStrategyType] = useState("") + + return ( +
+
+
+

AI 智能助手

+

利用AI技术分析数据并提供营销策略

+
+
+ + + + 数据分析 + 营销策略 + + + + + + AI 数据分析 + 使用AI分析用户数据并生成洞察报告 + + + {/* 第1步:选择用户分群 */} +
+
+

+
+ 1 +
+ 选择用户分群 +

+ {analysisStep > 1 ? ( + + ) : ( + + )} +
+ + {selectedUserGroup && ( +
+
+
+ + 已选择用户分群: + {selectedUserGroup} +
+ {analysisStep === 1 && ( + + )} +
+
+ )} +
+ + + + {/* 第2步:选择分析类型 */} +
2 ? "opacity-60" : "opacity-40") : ""}`} + > +
+

+
+ 2 +
+ 选择分析类型 +

+ {analysisStep > 2 && ( + + )} +
+ + {analysisStep >= 2 && ( +
+ setAnalysisType("behavior")} + > + + 用户行为分析 + + + 分析用户的行为模式、偏好和习惯 + + + {analysisType === "behavior" && 已选择} + + + setAnalysisType("value")} + > + + 用户估值分析 + + 分析用户的价值分布和潜在价值 + + {analysisType === "value" && 已选择} + + + setAnalysisType("churn")} + > + + 用户流失预警 + + + 预测可能流失的用户并提供挽留建议 + + + {analysisType === "churn" && 已选择} + + +
+ )} + + {analysisStep === 2 && analysisType && ( +
+ +
+ )} +
+ + + + {/* 第3步:分析参数设置 */} +
3 ? "opacity-60" : "opacity-40") : ""}`} + > +
+

+
+ 3 +
+ 分析参数设置 +

+ {analysisStep > 3 && ( + + )} +
+ + {analysisStep >= 3 && ( +
+
+ + +
+
+ + +
+
+ )} + + {analysisStep === 3 && ( +
+ +
+ )} +
+ + + + {/* 第4步:报告设置 */} +
+
+

+
+ 4 +
+ 报告设置 +

+
+ + {analysisStep >= 4 && ( +
+
+ + setEmailAddress(e.target.value)} + /> +
+
+ + +
+ {scheduleReport && ( +
+
+ + +
+
+ + +
+
+ )} +
+ )} +
+
+ + + + +
+ + + + 分析结果预览 + AI生成的数据分析结果 + + + {selectedUserGroup && analysisStep === 4 ? ( +
+ +

分析结果将在这里显示

+

点击"开始分析"按钮生成分析报告

+
+ + +
+
+ ) : ( +
+ +

请完成所有分析步骤

+ +
+ )} +
+
+
+ + + + + AI 营销策略 + 使用AI生成针对性的营销策略和建议 + + + {/* 第1步:选择目标用户分群 */} +
+
+

+
+ 1 +
+ 选择目标用户分群 +

+ {strategyStep > 1 ? ( + + ) : ( + + )} +
+ + {selectedUserGroup && ( +
+
+
+ + 已选择用户分群: + {selectedUserGroup} +
+ {strategyStep === 1 && ( + + )} +
+
+ )} +
+ + + + {/* 第2步:选择策略类型 */} +
2 ? "opacity-60" : "opacity-40") : ""}`} + > +
+

+
+ 2 +
+ 选择策略类型 +

+ {strategyStep > 2 && ( + + )} +
+ + {strategyStep >= 2 && ( +
+ setStrategyType("sales")} + > + + 销售预测 + + + 预测未来销售趋势并提供增长建议 + + + {strategyType === "sales" && 已选择} + + + setStrategyType("reach")} + > + + 用户触达策略 + + + 生成针对性的用户触达和转化策略 + + + {strategyType === "reach" && 已选择} + + + setStrategyType("content")} + > + + 内容营销策略 + + + 生成针对目标用户的内容营销策略 + + + {strategyType === "content" && 已选择} + + +
+ )} + + {strategyStep === 2 && strategyType && ( +
+ +
+ )} +
+ + + + {/* 第3步:策略参数设置 */} +
+
+

+
+ 3 +
+ 策略参数设置 +

+
+ + {strategyStep >= 3 && ( + <> +
+
+ + +
+
+ + +
+
+
+ +