diff --git a/Cunkebao/.babelrc b/Cunkebao/.babelrc new file mode 100644 index 00000000..1ff94f7e --- /dev/null +++ b/Cunkebao/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["next/babel"] +} diff --git a/Cunkebao/.gitignore b/Cunkebao/.gitignore index e39c24cf..f650315f 100644 --- a/Cunkebao/.gitignore +++ b/Cunkebao/.gitignore @@ -7,8 +7,6 @@ /.next/ /out/ -/.history/ - # production /build diff --git a/Cunkebao/app/admin/accounts/mobile/page.tsx b/Cunkebao/app/admin/accounts/mobile/page.tsx new file mode 100644 index 00000000..69b99964 --- /dev/null +++ b/Cunkebao/app/admin/accounts/mobile/page.tsx @@ -0,0 +1,250 @@ +"use client" + +import { useState } from "react" +import { Pencil, Trash2, Plus } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Checkbox } from "@/components/ui/checkbox" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Badge } from "@/components/ui/badge" + +interface MobileAccount { + id: string + name: string + phone: string + createdAt: string + status: "active" | "inactive" +} + +export default function MobileAccountsPage() { + const [accounts, setAccounts] = useState([ + { + id: "1", + name: "用户1", + phone: "13809076043", + createdAt: "2023-01-15", + status: "active", + }, + { + id: "2", + name: "用户2", + phone: "13819176143", + createdAt: "2023-02-15", + status: "inactive", + }, + { + id: "3", + name: "用户3", + phone: "13829276243", + createdAt: "2023-03-15", + status: "active", + }, + { + id: "4", + name: "用户4", + phone: "13839376343", + createdAt: "2023-04-15", + status: "inactive", + }, + { + id: "5", + name: "用户5", + phone: "13849476443", + createdAt: "2023-05-15", + status: "active", + }, + ]) + + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [newAccount, setNewAccount] = useState({ + name: "", + password: "", + phone: "", + role: "", + permissions: { + notifications: false, + dataView: false, + remoteControl: false, + }, + }) + + const handleCreateAccount = () => { + // 这里应该有API调用来创建账号 + const newId = (accounts.length + 1).toString() + setAccounts([ + ...accounts, + { + id: newId, + name: newAccount.name, + phone: newAccount.phone, + createdAt: new Date().toISOString().split("T")[0], + status: "active", + }, + ]) + setIsDialogOpen(false) + setNewAccount({ + name: "", + password: "", + phone: "", + role: "", + permissions: { + notifications: false, + dataView: false, + remoteControl: false, + }, + }) + } + + const handleDeleteAccount = (id: string) => { + setAccounts(accounts.filter((account) => account.id !== id)) + } + + return ( +
+
+

手机端账号列表

+ +
+ +
+ + + + 账号名称 + 手机号码 + 创建时间 + 状态 + 操作 + + + + {accounts.map((account) => ( + + {account.name} + {account.phone} + {account.createdAt} + + + {account.status === "active" ? "活跃" : "非活跃"} + + + + + + + + ))} + +
+
+ + + + + 新增手机端账号 + +
+
+ + setNewAccount({ ...newAccount, name: e.target.value })} + placeholder="请输入账号名称" + /> +
+
+ + setNewAccount({ ...newAccount, password: e.target.value })} + placeholder="请输入初始密码" + /> +
+
+ + setNewAccount({ ...newAccount, phone: e.target.value })} + placeholder="请输入手机号码" + /> +
+
+ + +
+
+ +
+
+ + setNewAccount({ + ...newAccount, + permissions: { ...newAccount.permissions, notifications: !!checked }, + }) + } + /> + +
+
+ + setNewAccount({ + ...newAccount, + permissions: { ...newAccount.permissions, dataView: !!checked }, + }) + } + /> + +
+
+ + setNewAccount({ + ...newAccount, + permissions: { ...newAccount.permissions, remoteControl: !!checked }, + }) + } + /> + +
+
+
+
+
+ +
+
+
+
+ ) +} diff --git a/Cunkebao/app/admin/accounts/operators/page.tsx b/Cunkebao/app/admin/accounts/operators/page.tsx new file mode 100644 index 00000000..30f695c2 --- /dev/null +++ b/Cunkebao/app/admin/accounts/operators/page.tsx @@ -0,0 +1,197 @@ +"use client" + +import { useState } from "react" +import { Pencil, Trash2, Plus } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Badge } from "@/components/ui/badge" + +interface OperatorAccount { + id: string + phone: string + nickname: string + deviceCount: number + friendCount: number + createdAt: string + status: "active" | "inactive" +} + +export default function OperatorAccountsPage() { + const [accounts, setAccounts] = useState([ + { + id: "1", + phone: "13809076043", + nickname: "操盘手1", + deviceCount: 1, + friendCount: 25, + createdAt: "2023-01-15", + status: "active", + }, + { + id: "2", + phone: "13819176143", + nickname: "操盘手2", + deviceCount: 2, + friendCount: 50, + createdAt: "2023-02-15", + status: "inactive", + }, + { + id: "3", + phone: "13829276243", + nickname: "操盘手3", + deviceCount: 3, + friendCount: 75, + createdAt: "2023-03-15", + status: "active", + }, + { + id: "4", + phone: "13839376343", + nickname: "操盘手4", + deviceCount: 4, + friendCount: 100, + createdAt: "2023-04-15", + status: "inactive", + }, + { + id: "5", + phone: "13849476443", + nickname: "操盘手5", + deviceCount: 5, + friendCount: 125, + createdAt: "2023-05-15", + status: "active", + }, + ]) + + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [newAccount, setNewAccount] = useState({ + phone: "", + nickname: "", + password: "", + }) + + const handleCreateAccount = () => { + // 这里应该有API调用来创建账号 + const newId = (accounts.length + 1).toString() + setAccounts([ + ...accounts, + { + id: newId, + phone: newAccount.phone, + nickname: newAccount.nickname, + deviceCount: 0, + friendCount: 0, + createdAt: new Date().toISOString().split("T")[0], + status: "active", + }, + ]) + setIsDialogOpen(false) + setNewAccount({ + phone: "", + nickname: "", + password: "", + }) + } + + const handleDeleteAccount = (id: string) => { + setAccounts(accounts.filter((account) => account.id !== id)) + } + + return ( +
+
+

操盘手账号列表

+ +
+ +
+ + + + 登录手机号 + 备注 (昵称) + 关联设备数量 + 微信好友数量 + 创建时间 + 状态 + 操作 + + + + {accounts.map((account) => ( + + {account.phone} + {account.nickname} + {account.deviceCount} + {account.friendCount} + {account.createdAt} + + + {account.status === "active" ? "活跃" : "非活跃"} + + + + + + + + ))} + +
+
+ + + + + 新增操盘手账号 + +
+
+ + setNewAccount({ ...newAccount, phone: e.target.value })} + placeholder="请输入手机号" + /> +
+
+ + setNewAccount({ ...newAccount, nickname: e.target.value })} + placeholder="请输入昵称" + /> +
+
+ + setNewAccount({ ...newAccount, password: e.target.value })} + placeholder="请输入初始密码" + /> +
+
+
+ +
+
+
+
+ ) +} diff --git a/Cunkebao/app/admin/components/AdminSidebar.tsx b/Cunkebao/app/admin/components/AdminSidebar.tsx new file mode 100644 index 00000000..52dba01a --- /dev/null +++ b/Cunkebao/app/admin/components/AdminSidebar.tsx @@ -0,0 +1,88 @@ +"use client" + +import { useState } from "react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { ChevronDown, Users, MessageSquare } from "lucide-react" + +export default function AdminSidebar() { + const pathname = usePathname() + const [expandedItems, setExpandedItems] = useState>({ + 账号管理: true, + }) + + const toggleExpand = (key: string) => { + setExpandedItems((prev) => ({ + ...prev, + [key]: !prev[key], + })) + } + + const isActive = (path: string) => pathname === path + + return ( +
+
+

存客宝管理系统

+
+ +
+ ) +} diff --git a/Cunkebao/app/admin/layout.tsx b/Cunkebao/app/admin/layout.tsx new file mode 100644 index 00000000..6b21f717 --- /dev/null +++ b/Cunkebao/app/admin/layout.tsx @@ -0,0 +1,28 @@ +"use client" + +import type React from "react" +import { Bell, User } from "lucide-react" +import { Button } from "@/components/ui/button" +import AdminSidebar from "./components/AdminSidebar" + +export default function AdminLayout({ children }: { children: React.ReactNode }) { + return ( +
+ +
+
+

运营端后台

+
+ + +
+
+
{children}
+
+
+ ) +} diff --git a/Cunkebao/app/admin/page.tsx b/Cunkebao/app/admin/page.tsx new file mode 100644 index 00000000..4ace7fdc --- /dev/null +++ b/Cunkebao/app/admin/page.tsx @@ -0,0 +1,66 @@ +"use client" + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { ArrowRight } from "lucide-react" +import Link from "next/link" + +export default function AdminDashboard() { + const stats = [ + { title: "总账号数", value: "42" }, + { title: "手机端账号", value: "28" }, + { title: "操盘手账号", value: "14" }, + { title: "今日活跃", value: "18" }, + ] + + return ( +
+

存客宝管理后台

+ +
+ {stats.map((stat, index) => ( + + + {stat.title} + + +

{stat.value}

+
+
+ ))} +
+ +
+ + + 手机端账号管理 + + +

管理存客宝手机端的用户账号,设置权限和功能。

+ + + +
+
+ + + + 操盘手账号管理 + + +

管理操盘手账号,查看关联设备和微信好友数量。

+ + + +
+
+
+
+ ) +} diff --git a/Cunkebao/app/api/acquisition/[planId]/orders/route.ts b/Cunkebao/app/api/acquisition/[planId]/orders/route.ts index 57754319..08bc6bc4 100644 --- a/Cunkebao/app/api/acquisition/[planId]/orders/route.ts +++ b/Cunkebao/app/api/acquisition/[planId]/orders/route.ts @@ -17,4 +17,3 @@ export async function POST(request: Request, { params }: { params: { planId: str return NextResponse.json({ success: false, message: "订单提交失败" }, { status: 500 }) } } - diff --git a/Cunkebao/app/api/auth.ts b/Cunkebao/app/api/auth.ts index c17c48c5..529d07d2 100644 --- a/Cunkebao/app/api/auth.ts +++ b/Cunkebao/app/api/auth.ts @@ -66,4 +66,3 @@ export async function publicFetch(url: string, options: RequestInit = {}) { throw error } } - diff --git a/Cunkebao/app/api/devices/route.ts b/Cunkebao/app/api/devices/route.ts index 6de78c12..c7a1de4d 100644 --- a/Cunkebao/app/api/devices/route.ts +++ b/Cunkebao/app/api/devices/route.ts @@ -79,4 +79,3 @@ export async function GET(request: Request) { ) } } - diff --git a/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx b/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx new file mode 100644 index 00000000..b37bd496 --- /dev/null +++ b/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx @@ -0,0 +1,377 @@ +"use client" + +import { useState } from "react" +import { ChevronLeft, Copy, Check, Info } from "lucide-react" +import { Button } from "@/components/ui/button" +import { useRouter } from "next/navigation" +import { useToast } from "@/components/ui/use-toast" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { getApiGuideForScenario } from "@/docs/api-guide" +import { Badge } from "@/components/ui/badge" +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion" + +export default function ApiDocPage({ params }: { params: { channel: string; id: string } }) { + const router = useRouter() + const { toast } = useToast() + const [copiedExample, setCopiedExample] = useState(null) + + const apiGuide = getApiGuideForScenario(params.id, params.channel) + + const copyToClipboard = (text: string, exampleId: string) => { + navigator.clipboard.writeText(text) + setCopiedExample(exampleId) + + toast({ + title: "已复制代码", + description: "代码示例已复制到剪贴板", + }) + + setTimeout(() => { + setCopiedExample(null) + }, 2000) + } + + return ( +
+
+
+
+ +
+

{apiGuide.title}

+

API接口文档

+
+
+
+ +
+
+
+ +
+ {/* API密钥卡片 */} + + +
+
+ +
+
+ API密钥 + 用于身份验证,请妥善保管,不要在客户端代码中暴露它 +
+
+
+ +
+
+
+ api_1_xqbint74 + +
+
+ +
+
+ +
+

安全提示

+

+ 请妥善保管您的API密钥,不要在客户端代码中暴露它。建议在服务器端使用该接口。 +

+
+
+
+
+
+
+ + {/* 接口地址卡片 */} + + + 接口地址 + 使用此接口直接导入客户资料到该获客计划,支持多种编程语言 + + +
+
+
+ POST 请求地址 + +
+ + https://kzminfd0rplrm7owmj4b.lite.vusercontent.net/api/scenarios/post + +
+ +
+
+

必要参数

+
+ + name (姓名) + + + phone (电话) + +
+
+
+

可选参数

+
+ + source (来源) + + + remark (备注) + + + tags (标签) + +
+
+
+
+
+
+ + {/* 接口文档卡片 */} + + + 接口文档 + 详细的API规范和集成指南 + + +
+ + +
+
+
+ + {/* 快速测试卡片 */} + + + 快速测试 + 使用以下URL可以快速测试接口是否正常工作 + + +
+
+ 测试URL + +
+ + https://kzminfd0rplrm7owmj4b.lite.vusercontent.net/api/scenarios/poster/1/webhook?name=测试客户&phone=13800138000 + +
+
+

💡 点击上方链接或复制到浏览器中访问,即可快速测试接口连通性

+
+
+
+ + {/* 详细文档手风琴 */} +
+ + {apiGuide.endpoints.map((endpoint, index) => ( + + +
+ {endpoint.method} + {endpoint.url} +
+
+ +
+

{endpoint.description}

+ +
+

请求头

+
+ {endpoint.headers.map((header, i) => ( +
+ + {header.required ? "*" : ""} + {header.name} + +
+

{header.value}

+

{header.description}

+
+
+ ))} +
+
+ +
+

请求参数

+
+ {endpoint.parameters.map((param, i) => ( +
+ + {param.required ? "*" : ""} + {param.name} + +
+

+ {param.type} +

+

{param.description}

+
+
+ ))} +
+
+ +
+

响应示例

+
+                        {JSON.stringify(endpoint.response, null, 2)}
+                      
+
+
+
+
+ ))} +
+
+ + {/* 代码示例 */} + + + 代码示例 + 以下是不同编程语言的接口调用示例 + + + + + {apiGuide.examples.map((example) => ( + + {example.title} + + ))} + + + {apiGuide.examples.map((example) => ( + +
+
{example.code}
+ +
+
+ ))} +
+
+
+ + {/* 集成指南 */} +
+

集成指南

+ + + + 集简云平台集成 + + +
    +
  1. 登录集简云平台
  2. +
  3. 导航至"应用集成" > "外部接口"
  4. +
  5. 选择"添加新接口",输入存客宝接口信息
  6. +
  7. 配置回调参数,将"X-API-KEY"设置为您的API密钥
  8. +
  9. + 设置接口URL为: + {apiGuide.endpoints[0].url} +
  10. +
  11. 映射必要字段(name, phone等)
  12. +
  13. 保存并启用集成
  14. +
+
+
+ + + + 问题排查 + + +
+

接口认证失败

+

+ 请确保X-API-KEY正确无误,此密钥区分大小写。如需重置密钥,请联系管理员。 +

+
+ +
+

数据格式错误

+

+ 确保所有必填字段已提供,并且字段类型正确。特别是电话号码格式需符合标准。 +

+
+ +
+

请求频率限制

+

+ 单个API密钥每分钟最多可发送30个请求,超过限制将被暂时限制。对于大批量数据,请使用批量接口。 +

+
+
+
+
+
+
+ ) +} diff --git a/Cunkebao/app/api/scenarios/route.ts b/Cunkebao/app/api/scenarios/route.ts index f82d21d8..7b5e7c0a 100644 --- a/Cunkebao/app/api/scenarios/route.ts +++ b/Cunkebao/app/api/scenarios/route.ts @@ -71,4 +71,3 @@ export async function GET(request: Request) { ) } } - diff --git a/Cunkebao/app/api/users/route.ts b/Cunkebao/app/api/users/route.ts index 31046e37..ace987c7 100644 --- a/Cunkebao/app/api/users/route.ts +++ b/Cunkebao/app/api/users/route.ts @@ -270,4 +270,3 @@ export async function GET(request: Request) { }, }) } - diff --git a/Cunkebao/app/clientLayout.tsx b/Cunkebao/app/clientLayout.tsx index cb105ad8..0054c505 100644 --- a/Cunkebao/app/clientLayout.tsx +++ b/Cunkebao/app/clientLayout.tsx @@ -1,34 +1,13 @@ "use client" import "./globals.css" -import BottomNav from "./components/BottomNav" import "regenerator-runtime/runtime" import type React from "react" import ErrorBoundary from "./components/ErrorBoundary" -import { VideoTutorialButton } from "@/components/VideoTutorialButton" import { AuthProvider } from "@/app/components/AuthProvider" -import { usePathname } from "next/navigation" +import BottomNav from "./components/BottomNav" -// 创建一个包装组件来使用 usePathname hook -function LayoutContent({ children }: { children: React.ReactNode }) { - const pathname = usePathname() - - // 只在主页路径显示底部导航栏 - const showBottomNav = - pathname === "/" || pathname === "/devices" || pathname === "/content" || pathname === "/profile" - - return ( -
-
- {children} - {showBottomNav && } - {showBottomNav && } -
-
- ) -} - -export default function RootLayout({ +export default function ClientLayout({ children, }: { children: React.ReactNode @@ -38,11 +17,16 @@ export default function RootLayout({ - {children} +
+ {children} + {/* 移除条件渲染,确保底部导航始终显示 */} +
+ +
+
) } - diff --git a/Cunkebao/app/components-demo/loading.tsx b/Cunkebao/app/components-demo/loading.tsx new file mode 100644 index 00000000..a69af020 --- /dev/null +++ b/Cunkebao/app/components-demo/loading.tsx @@ -0,0 +1,34 @@ +import { Card, CardContent, CardHeader } from "@/components/ui/card" + +export default function ComponentsDemoLoading() { + return ( +
+
+
+
+
+ +
+
+ {Array.from({ length: 6 }).map((_, i) => ( +
+ ))} +
+ +
+ {Array.from({ length: 6 }).map((_, i) => ( + + +
+
+ + +
+ + + ))} +
+
+
+ ) +} diff --git a/Cunkebao/app/components-demo/page.tsx b/Cunkebao/app/components-demo/page.tsx new file mode 100644 index 00000000..7d99363f --- /dev/null +++ b/Cunkebao/app/components-demo/page.tsx @@ -0,0 +1,493 @@ +"use client" + +import type React from "react" +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 { Badge } from "@/components/ui/badge" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { Switch } from "@/components/ui/switch" +import { Checkbox } from "@/components/ui/checkbox" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Progress } from "@/components/ui/progress" +import { Slider } from "@/components/ui/slider" +import { Calendar } from "@/components/ui/calendar" +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Separator } from "@/components/ui/separator" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" + +import { CalendarIcon, Code, Copy, Check, Smartphone, Users, TrendingUp, Activity } from "lucide-react" + +/** + * 组件库展示页面 + * 展示所有可用的UI组件和自定义组件 + */ +export default function ComponentsDemo() { + return ( +
+
+

存客宝组件库

+

展示项目中所有可用的UI组件和自定义组件,包含使用示例和代码演示

+
+ + + + 基础组件 + 表单组件 + 数据展示 + 反馈组件 + 导航组件 + 自定义组件 + + + {/* 基础组件 */} + + +
+ + + + + + + + +
+
+ + +
+ 默认 + 次要 + 边框 + 危险 + 成功 + 警告 +
+
+ + +
+ + + CN + + + AB + + + XY + +
+
+ + +
+
水平分隔符
+ +
垂直分隔符
+
+ 左侧内容 + + 右侧内容 +
+
+
+
+ + {/* 表单组件 */} + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +