diff --git a/Cunkebao/.babelrc b/Cunkebao/.babelrc deleted file mode 100644 index 1ff94f7e..00000000 --- a/Cunkebao/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["next/babel"] -} diff --git a/Cunkebao/.eslintrc.json b/Cunkebao/.eslintrc.json deleted file mode 100644 index d7ffa278..00000000 --- a/Cunkebao/.eslintrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "root": true, - "extends": [ - "next/core-web-vitals", - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "plugins": ["@typescript-eslint"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, - "rules": { - "@typescript-eslint/no-unused-vars": ["warn"], - "react/react-in-jsx-scope": "off" - } -} \ No newline at end of file diff --git a/Cunkebao/.gitignore b/Cunkebao/.gitignore index f650315f..4d29575d 100644 --- a/Cunkebao/.gitignore +++ b/Cunkebao/.gitignore @@ -2,26 +2,22 @@ # dependencies /node_modules +/.pnp +.pnp.js -# next.js -/.next/ -/out/ +# testing +/coverage # production /build -# debug +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + 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/Cunkebao/.htaccess b/Cunkebao/.htaccess deleted file mode 100644 index 0c54fb9f..00000000 --- a/Cunkebao/.htaccess +++ /dev/null @@ -1 +0,0 @@ -# 请将伪静态规则或自定义Apache配置填写到此处 diff --git a/Cunkebao/.well-known/acme-challenge/nKNDFrnOs9-cf7FZzW7Cg4MpIxhtE8pZ1etiT_7NPWA b/Cunkebao/.well-known/acme-challenge/nKNDFrnOs9-cf7FZzW7Cg4MpIxhtE8pZ1etiT_7NPWA deleted file mode 100644 index 2f056a63..00000000 --- a/Cunkebao/.well-known/acme-challenge/nKNDFrnOs9-cf7FZzW7Cg4MpIxhtE8pZ1etiT_7NPWA +++ /dev/null @@ -1 +0,0 @@ -nKNDFrnOs9-cf7FZzW7Cg4MpIxhtE8pZ1etiT_7NPWA.2wKeISBpPoGcCt72TdgVrBMxtnnieXof2OZkRdHKCJI \ No newline at end of file diff --git a/Cunkebao/README.md b/Cunkebao/README.md index 0532c5f3..be60268f 100644 --- a/Cunkebao/README.md +++ b/Cunkebao/README.md @@ -1,73 +1,123 @@ -# 存客宝 - 智能获客管理平台 +# 内客宝 - 智能获客管理平台 ## 📋 项目简介 -存客宝是一个专业的微信获客和流量管理平台,提供智能化的客户获取、管理和运营解决方案。平台集成了多种自动化工具,帮助企业高效管理微信营销活动。 +内客宝是一个专业的微信获客和流量管理平台,基于 React 技术栈构建。平台提供智能化的客户获取、管理和运营解决方案,集成了多种自动化工具,帮助企业高效管理微信营销活动。 -## 🚀 技术栈 +## 🚀 技术栈详解 -### 前端框架 -- **Next.js 15.3.5** - React 全栈框架,支持 SSR/SSG -- **React 19.1.0** - 用户界面库 -- **TypeScript 5** - 类型安全的 JavaScript 超集 +### 核心框架 +- **React 18.2.0** - 现代化的用户界面库 +- **TypeScript 4.9.5** - 类型安全的 JavaScript 超集 +- **Create React App (CRA) 5.0.1** - React 应用脚手架 +- **React Router DOM 6.20.0** - 客户端路由管理 + +### 构建工具 +- **CRACO 7.1.0** - Create React App Configuration Override + - 支持自定义 webpack 配置 + - 路径别名配置 + - 构建优化 ### UI 组件库 -- **shadcn/ui** - 基于 Radix UI 的现代化组件库 +- **Radix UI** - 无样式的可访问组件库 + - 完整的组件生态系统(30+ 组件) + - 优秀的无障碍访问支持 + - 高度可定制 - **Tailwind CSS 3.4.17** - 实用优先的 CSS 框架 -- **Lucide React** - 精美的图标库 -- **Framer Motion** - 动画库 + - 响应式设计支持 + - 自定义主题配置 + - 原子化 CSS 类 -### 状态管理与表单 -- **React Hook Form** - 高性能表单库 -- **Zod** - TypeScript 优先的模式验证 -- **@hookform/resolvers** - 表单验证解析器 +### 图标和样式 +- **Lucide React 0.454.0** - 精美的图标库 +- **Tailwind CSS Animate** - CSS 动画库 +- **Class Variance Authority** - 组件变体管理 +- **Tailwind Merge** - Tailwind 类名合并工具 + +### 状态管理和表单 +- **React Hook Form 7.54.1** - 高性能表单库 +- **Zod 3.24.1** - TypeScript 优先的模式验证 +- **@hookform/resolvers 3.9.1** - 表单验证解析器 ### 数据可视化 - **Recharts** - 基于 React 的图表库 -- **Chart.js** - 灵活的图表库 +- **Chart.js 4.5.0** - 灵活的图表库 +- **@ant-design/plots** - Ant Design 图表组件 + +### HTTP 请求和数据处理 +- **Axios 1.6.0** - HTTP 客户端 +- **Crypto-js 4.2.0** - 加密库 +- **Date-fns** - 日期处理库 +- **XLSX 0.18.5** - Excel 文件处理 + +### 通知和反馈 +- **React Hot Toast 2.5.2** - 轻量级通知库 +- **Sonner 1.7.4** - 现代化 Toast 组件 + +### 高级组件 +- **@tanstack/react-table** - 功能强大的表格组件 +- **Embla Carousel React 8.5.1** - 轮播组件 +- **React Resizable Panels 2.1.7** - 可调整大小的面板 +- **Vaul 0.9.6** - 抽屉组件 +- **Input OTP 1.4.1** - OTP 输入组件 +- **React Day Picker** - 日期选择器 ### 开发工具 +- **PostCSS 8** - CSS 后处理器 +- **Autoprefixer 10.4.20** - CSS 前缀自动添加 - **ESLint** - 代码质量检查 -- **PostCSS** - CSS 后处理器 -- **Autoprefixer** - CSS 前缀自动添加 +- **Jest** - 单元测试框架 +- **Testing Library** - React 测试工具 ## 📁 项目结构 ``` -Cunkebao/ -├── app/ # Next.js App Router 页面 -│ ├── api/ # API 路由 -│ ├── components/ # 页面级组件 -│ ├── workspace/ # 工作台模块 -│ ├── scenarios/ # 场景管理 -│ ├── devices/ # 设备管理 -│ ├── content/ # 内容管理 -│ └── ... -├── components/ # 全局组件 -│ ├── ui/ # shadcn/ui 组件 -│ └── ... -├── lib/ # 工具库 -│ ├── api/ # API 封装 -│ └── utils.ts # 通用工具函数 -├── hooks/ # 自定义 Hooks -├── types/ # TypeScript 类型定义 -└── public/ # 静态资源 +nkebao/ +├── public/ # 静态资源 +├── src/ # 源代码 +│ ├── api/ # API 接口封装 +│ ├── components/ # 全局组件 +│ │ ├── ui/ # UI 基础组件 +│ │ └── icons/ # 图标组件 +│ ├── config/ # 配置文件 +│ ├── contexts/ # React Context +│ ├── hooks/ # 自定义 Hooks +│ ├── pages/ # 页面组件 +│ │ ├── workspace/ # 工作台模块 +│ │ │ ├── auto-like/ # 自动点赞 +│ │ │ ├── auto-group/ # 自动建群 +│ │ │ ├── group-push/ # 群消息推送 +│ │ │ ├── moments-sync/ # 朋友圈同步 +│ │ │ ├── ai-assistant/ # AI 对话助手 +│ │ │ └── traffic-distribution/ # 流量分发 +│ │ ├── devices/ # 设备管理 +│ │ ├── scenarios/ # 场景管理 +│ │ ├── content/ # 内容管理 +│ │ └── ... +│ ├── types/ # TypeScript 类型定义 +│ ├── utils/ # 工具函数 +│ ├── App.tsx # 应用根组件 +│ └── index.tsx # 应用入口 +├── craco.config.js # CRACO 配置 +├── tailwind.config.js # Tailwind CSS 配置 +├── tsconfig.json # TypeScript 配置 +└── package.json # 项目依赖 ``` ## 🎯 核心功能模块 ### 工作台 (Workspace) -- **自动点赞** - 智能点赞管理 -- **朋友圈同步** - 内容同步工具 -- **群消息推送** - 群组消息管理 -- **AI 对话助手** - 智能客服系统 -- **自动建群** - 群组自动化管理 -- **流量分发** - 流量分配策略 +- **自动点赞** - 智能点赞管理和配置 +- **自动建群** - 群组自动化创建和管理 +- **群消息推送** - 群组消息批量发送 +- **朋友圈同步** - 内容同步和发布 +- **AI 对话助手** - 智能客服和对话管理 +- **流量分发** - 流量分配和策略管理 ### 设备管理 (Devices) -- 设备状态监控 -- 设备配置管理 +- 设备状态监控和配置 - 设备性能分析 +- 设备权限管理 ### 场景管理 (Scenarios) - 营销场景配置 @@ -79,61 +129,78 @@ Cunkebao/ - 内容模板管理 - 内容发布调度 -### 用户管理 (Users) -- 用户权限管理 -- 角色分配 -- 用户行为分析 +### 其他模块 +- 用户管理 (Users) +- 订单管理 (Orders) +- 流量池管理 (Traffic Pool) +- 联系人导入 (Contact Import) ## 🛠️ 开发指南 ### 环境要求 -- Node.js 18+ -- npm 或 pnpm +- **Node.js** 16+ +- **npm** 或 **yarn** ### 安装依赖 ```bash +# 使用 npm npm install -# 或 -pnpm install + +# 使用 yarn +yarn install ``` ### 开发环境启动 ```bash -npm run dev -# 或 -pnpm dev +# 使用 npm +npm start + +# 使用 yarn +yarn start ``` ### 构建生产版本 ```bash +# 使用 npm npm run build -npm start + +# 使用 yarn +yarn build ``` -### 代码检查 +### 运行测试 ```bash -npm run lint +# 使用 npm +npm test + +# 使用 yarn +yarn test ``` ## 🔧 配置说明 -### 环境变量 -创建 `.env.local` 文件: -```env -NEXT_PUBLIC_API_BASE_URL=your_api_base_url -CUSTOM_KEY=your_custom_key +### 路径别名配置 +项目使用 CRACO 配置了路径别名: +```javascript +'@': path.resolve(__dirname, 'src'), +'@/components': path.resolve(__dirname, 'src/components'), +'@/api': path.resolve(__dirname, 'src/api'), +'@/types': path.resolve(__dirname, 'src/types'), +'@/hooks': path.resolve(__dirname, 'src/hooks'), +'@/utils': path.resolve(__dirname, 'src/utils'), +'@/styles': path.resolve(__dirname, 'src/styles'), +'@/pages': path.resolve(__dirname, 'src/pages'), ``` -### Next.js 配置 -- 支持图片优化和多种格式 -- 配置了 API 代理和 CORS -- 启用了 SWC 压缩 -- 支持包分析工具 - ### Tailwind CSS 配置 -- 支持暗色模式 -- 自定义颜色系统 -- 响应式设计支持 +- 自定义字体大小和间距 +- 响应式断点配置 +- 主题颜色系统 + +### TypeScript 配置 +- 严格模式启用 +- 路径映射配置 +- JSX 支持 ## 📱 响应式设计 @@ -149,9 +216,9 @@ CUSTOM_KEY=your_custom_key - 一致的用户体验 - 无障碍访问支持 -### 组件库 -- 50+ 个预构建组件 -- 完整的表单组件 +### 组件库特点 +- 基于 Radix UI 的高质量组件 +- 完整的表单组件系统 - 数据展示组件 - 导航和布局组件 @@ -160,21 +227,27 @@ CUSTOM_KEY=your_custom_key - 身份验证和授权 - API 请求拦截 - 数据验证和清理 -- CORS 配置 +- 加密功能支持 ## 📊 性能优化 - 代码分割和懒加载 -- 图片优化 +- 组件优化 - 缓存策略 - 包大小优化 +## 🧪 测试策略 + +- 单元测试 (Jest + Testing Library) +- 组件测试 +- 集成测试支持 + ## 🤝 贡献指南 1. Fork 项目 -2. 创建功能分支 -3. 提交更改 -4. 推送到分支 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 创建 Pull Request ## 📄 许可证 @@ -187,5 +260,7 @@ CUSTOM_KEY=your_custom_key --- +**项目名称**: 内客宝 (nkebao2) **版本**: 0.1.0 +**技术栈**: React + TypeScript + CRA + Tailwind CSS **最后更新**: 2024年12月 \ No newline at end of file diff --git a/Cunkebao/api/devices.ts b/Cunkebao/api/devices.ts deleted file mode 100644 index 64da66da..00000000 --- a/Cunkebao/api/devices.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { api } from "@/lib/api"; -import type { - ApiResponse, - Device, - DeviceStats, - DeviceTaskRecord, - PaginatedResponse, - QueryDeviceParams, - CreateDeviceParams, - UpdateDeviceParams, - DeviceStatus, - ServerDevice, - ServerDevicesResponse -} from "@/types/device" - -const API_BASE = "/api/devices" - -// 获取设备列表 - 连接到服务器/v1/devices接口 -export const fetchDeviceList = async (page: number = 1, limit: number = 20, keyword?: string): Promise => { - const params = new URLSearchParams(); - params.append('page', page.toString()); - params.append('limit', limit.toString()); - - if (keyword) { - params.append('keyword', keyword); - } - - return api.get(`/v1/devices?${params.toString()}`); -}; - -// 获取设备详情 - 连接到服务器/v1/devices/:id接口 -export const fetchDeviceDetail = async (id: string | number): Promise> => { - return api.get>(`/v1/devices/${id}`); -}; - -// 获取设备关联的微信账号 -export const fetchDeviceRelatedAccounts = async (id: string | number): Promise> => { - return api.get>(`/v1/wechats/related-device/${id}`); -}; - -// 获取设备操作记录 -export const fetchDeviceHandleLogs = async (id: string | number, page: number = 1, limit: number = 10): Promise> => { - return api.get>(`/v1/devices/${id}/handle-logs?page=${page}&limit=${limit}`); -}; - -// 更新设备任务配置 -export const updateDeviceTaskConfig = async ( - config: { - deviceId: string | number; - autoAddFriend?: boolean; - autoReply?: boolean; - momentsSync?: boolean; - aiChat?: boolean; - } -): Promise> => { - return api.post>(`/v1/devices/task-config`, { - ...config - }); -}; - -// 删除设备 -export const deleteDevice = async (id: number): Promise> => { - return api.delete>(`/v1/devices/${id}`); -}; - -// 设备管理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>> { - // 创建一个新对象,用于构建URLSearchParams - const queryParams: Record = {}; - - // 按需将params中的属性添加到queryParams - if (params.keyword) queryParams.keyword = params.keyword; - if (params.status) queryParams.status = params.status; - if (params.type) queryParams.type = params.type; - if (params.page) queryParams.page = params.page.toString(); - if (params.pageSize) queryParams.pageSize = params.pageSize.toString(); - - // 特殊处理需要JSON序列化的属性 - if (params.tags) queryParams.tags = JSON.stringify(params.tags); - if (params.dateRange) queryParams.dateRange = JSON.stringify(params.dateRange); - - // 构建查询字符串 - const queryString = new URLSearchParams(queryParams).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/Cunkebao/api/route.ts b/Cunkebao/api/route.ts deleted file mode 100644 index f82d21d8..00000000 --- a/Cunkebao/api/route.ts +++ /dev/null @@ -1,74 +0,0 @@ -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/Cunkebao/api/scenarios.ts b/Cunkebao/api/scenarios.ts deleted file mode 100644 index ed77aeb5..00000000 --- a/Cunkebao/api/scenarios.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { request } from "@/lib/api" - -export interface ApiResponse { - code: number; - msg: string; - data: T | null; -} - -// 服务器返回的场景数据类型 -export interface SceneItem { - id: number; - name: string; - image: string; - status: number; - createTime: number; - updateTime: number | null; - deleteTime: number | null; -} - -// 服务器返回的场景列表响应类型 -export interface ScenesResponse { - code: number; - msg: string; - data: SceneItem[]; -} - -// 前端使用的场景数据类型 -export interface Channel { - id: string; - name: string; - icon: string; - stats: { - daily: number; - growth: number; - }; - link?: string; - plans?: Plan[]; -} - -// 计划类型 -export interface Plan { - id: string; - name: string; - isNew?: boolean; - status: "active" | "paused" | "completed"; - acquisitionCount: number; -} - -/** - * 获取获客场景列表 - * - * @param params 查询参数 - * @returns 获客场景列表 - */ -export const fetchScenes = async (params: { - page?: number; - limit?: number; - keyword?: string; -} = {}): Promise => { - const { page = 1, limit = 10, keyword = "" } = params; - - const queryParams = new URLSearchParams(); - queryParams.append("page", String(page)); - queryParams.append("limit", String(limit)); - - if (keyword) { - queryParams.append("keyword", keyword); - } - - try { - return await request(`/v1/plan/scenes?${queryParams.toString()}`); - } catch (error) { - console.error("Error fetching scenes:", error); - // 返回一个错误响应 - return { - code: 500, - msg: "获取场景列表失败", - data: [] - }; - } -}; - -/** - * 将服务器返回的场景数据转换为前端展示需要的格式 - * - * @param item 服务器返回的场景数据 - * @returns 前端展示的场景数据 - */ -export const transformSceneItem = (item: SceneItem): Channel => { - // 为每个场景生成随机的"今日"数据和"增长百分比" - const dailyCount = Math.floor(Math.random() * 100); - const growthPercent = Math.floor(Math.random() * 40) - 10; // -10% 到 30% 的随机值 - - // 默认图标(如果服务器没有返回) - const defaultIcon = "/assets/icons/poster-icon.svg"; - - return { - id: String(item.id), - name: item.name, - icon: item.image || defaultIcon, - stats: { - daily: dailyCount, - growth: growthPercent - } - }; -}; - -/** - * 获取场景详情 - * - * @param id 场景ID - * @returns 场景详情 - */ -export const fetchSceneDetail = async (id: string | number): Promise> => { - try { - return await request>(`/v1/plan/scenes/${id}`); - } catch (error) { - console.error("Error fetching scene detail:", error); - return { - code: 500, - msg: "获取场景详情失败", - data: null - }; - } -}; - diff --git a/Cunkebao/api/wechat-accounts.ts b/Cunkebao/api/wechat-accounts.ts deleted file mode 100644 index 393e1d8b..00000000 --- a/Cunkebao/api/wechat-accounts.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { api } from "@/lib/api"; -import { - ServerWechatAccountsResponse, - QueryWechatAccountParams, -} from "@/types/wechat-account"; - -// 添加接口返回数据类型定义 -interface WechatAccountSummary { - accountAge: string; - activityLevel: { - allTimes: number; - dayTimes: number; - }; - accountWeight: { - scope: number; - ageWeight: number; - activityWeigth: number; - restrictWeight: number; - realNameWeight: number; - }; - statistics: { - todayAdded: number; - addLimit: number; - }; - restrictions: { - id: number; - level: string; - reason: string; - date: string; - }[]; -} - -interface WechatAccountSummaryResponse { - code: number; - msg: string; - data: WechatAccountSummary; -} - -/** - * 获取微信账号列表 - * @param params 查询参数 - * @returns 微信账号列表响应 - */ -export const fetchWechatAccountList = async (params: QueryWechatAccountParams = {}): Promise => { - const queryParams = new URLSearchParams(); - - // 添加查询参数 - if (params.page) queryParams.append('page', params.page.toString()); - if (params.limit) queryParams.append('limit', params.limit.toString()); - if (params.keyword) queryParams.append('nickname', params.keyword); // 使用nickname作为关键词搜索参数 - if (params.sort) queryParams.append('sort', params.sort); - if (params.order) queryParams.append('order', params.order); - - // 发起API请求 - return api.get(`/v1/wechats?${queryParams.toString()}`); -}; - -/** - * 刷新微信账号状态 - * @returns 刷新结果 - */ -export const refreshWechatAccounts = async (): Promise<{ code: number; msg: string; data: any }> => { - return api.put<{ code: number; msg: string; data: any }>('/v1/wechats/refresh', {}); -}; - -/** - * 执行微信好友转移 - * @param sourceId 源微信账号ID - * @param targetId 目标微信账号ID - * @returns 转移结果 - */ -export const transferWechatFriends = async (sourceId: string | number, targetId: string | number): Promise<{ code: number; msg: string; data: any }> => { - return api.post<{ code: number; msg: string; data: any }>('/v1/wechats/transfer-friends', { - source_id: sourceId, - target_id: targetId - }); -}; - -/** - * 将服务器返回的微信账号数据转换为前端使用的格式 - * @param serverAccount 服务器返回的微信账号数据 - * @returns 前端使用的微信账号数据 - */ -export const transformWechatAccount = (serverAccount: any): import("@/types/wechat-account").WechatAccount => { - // 从deviceInfo中提取设备信息 - let deviceId = ''; - let deviceName = ''; - - if (serverAccount.deviceInfo) { - // 尝试解析设备信息字符串 - const deviceInfo = serverAccount.deviceInfo.split(' '); - if (deviceInfo.length > 0) { - // 提取数字部分作为设备ID,确保是整数 - const possibleId = deviceInfo[0].trim(); - // 验证是否为数字 - deviceId = /^\d+$/.test(possibleId) ? possibleId : ''; - - // 提取设备名称 - if (deviceInfo.length > 1) { - deviceName = deviceInfo[1] ? deviceInfo[1].replace(/[()]/g, '').trim() : ''; - } - } - } - - // 如果从deviceInfo无法获取有效的设备ID,使用imei作为备选 - if (!deviceId && serverAccount.imei) { - deviceId = serverAccount.imei; - } - - // 如果仍然没有设备ID,使用微信账号的ID作为最后的备选 - if (!deviceId && serverAccount.id) { - deviceId = serverAccount.id.toString(); - } - - // 如果没有设备名称,使用备用名称 - if (!deviceName) { - deviceName = serverAccount.deviceMemo || '未命名设备'; - } - - // 假设每天最多可添加20个好友 - const maxDailyAdds = 20; - const todayAdded = serverAccount.todayNewFriendCount || 0; - - return { - id: serverAccount.id.toString(), - avatar: serverAccount.avatar || '', - nickname: serverAccount.nickname || serverAccount.accountNickname || '未命名', - wechatId: serverAccount.wechatId || '', - deviceId, - deviceName, - friendCount: serverAccount.totalFriend || 0, - todayAdded, - remainingAdds: serverAccount.canAddFriendCount || (maxDailyAdds - todayAdded), - maxDailyAdds, - status: serverAccount.wechatAlive === 1 ? "normal" : "abnormal" as "normal" | "abnormal", - lastActive: new Date().toLocaleString() // 服务端未提供,使用当前时间 - }; -}; - -/** - * 获取微信好友列表 - * @param wechatId 微信账号ID - * @param page 页码 - * @param pageSize 每页数量 - * @param searchQuery 搜索关键词 - * @returns 好友列表数据 - */ -export const fetchWechatFriends = async (wechatId: string, page: number = 1, pageSize: number = 20, searchQuery: string = '') => { - try { - return api.get(`/v1/wechats/${wechatId}/friends?page=${page}&limit=${pageSize}${searchQuery ? `&search=${searchQuery}` : ''}`, true); - } catch (error) { - console.error("获取好友列表失败:", error); - throw error; - } -}; - -/** - * 获取微信账号概览信息 - * @param id 微信账号ID - * @returns 微信账号概览信息 - */ -export const fetchWechatAccountSummary = async (wechatIdid: string): Promise => { - try { - return api.get(`/v1/wechats/${wechatIdid}/summary`); - } catch (error) { - console.error("获取账号概览失败:", error); - throw error; - } -}; - -/** - * 获取好友详情信息 - * @param wechatId 微信账号ID - * @param friendId 好友ID - * @returns 好友详情信息 - */ -export interface WechatFriendDetail { - id: number; - avatar: string; - nickname: string; - region: string; - wechatId: string; - addDate: string; - tags: string[]; - playDate: string; - memo: string; - source: string; -} - -interface WechatFriendDetailResponse { - code: number; - msg: string; - data: WechatFriendDetail; -} - -export const fetchWechatFriendDetail = async (wechatId: string): Promise => { - try { - return api.get(`/v1/wechats/${wechatId}`); - } catch (error) { - console.error("获取好友详情失败:", error); - throw error; - } -}; \ No newline at end of file diff --git a/Cunkebao/app/admin/accounts/mobile/page.tsx b/Cunkebao/app/admin/accounts/mobile/page.tsx deleted file mode 100644 index 69b99964..00000000 --- a/Cunkebao/app/admin/accounts/mobile/page.tsx +++ /dev/null @@ -1,250 +0,0 @@ -"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 deleted file mode 100644 index 30f695c2..00000000 --- a/Cunkebao/app/admin/accounts/operators/page.tsx +++ /dev/null @@ -1,197 +0,0 @@ -"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 deleted file mode 100644 index 52dba01a..00000000 --- a/Cunkebao/app/admin/components/AdminSidebar.tsx +++ /dev/null @@ -1,88 +0,0 @@ -"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 deleted file mode 100644 index 6b21f717..00000000 --- a/Cunkebao/app/admin/layout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -"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 deleted file mode 100644 index 4ace7fdc..00000000 --- a/Cunkebao/app/admin/page.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"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 deleted file mode 100644 index 08bc6bc4..00000000 --- a/Cunkebao/app/api/acquisition/[planId]/orders/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NextResponse } from "next/server" -import type { OrderFormData } from "@/types/acquisition" - -export async function POST(request: Request, { params }: { params: { planId: string } }) { - try { - const data: OrderFormData = await request.json() - - // 这里应该添加实际的数据库存储逻辑 - console.log("Received order:", data, "for plan:", params.planId) - - // 模拟成功响应 - return NextResponse.json({ - success: true, - message: "订单已成功提交", - }) - } catch (error) { - return NextResponse.json({ success: false, message: "订单提交失败" }, { status: 500 }) - } -} diff --git a/Cunkebao/app/api/auth.ts b/Cunkebao/app/api/auth.ts deleted file mode 100644 index 529d07d2..00000000 --- a/Cunkebao/app/api/auth.ts +++ /dev/null @@ -1,68 +0,0 @@ -// API请求工具函数 -import { toast } from "@/components/ui/use-toast" - -const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://api.example.com" - -// 带有认证的请求函数 -export async function authFetch(url: string, options: RequestInit = {}) { - const token = localStorage.getItem("token") - - // 合并headers - let headers = { ...options.headers } - - // 如果有token,添加到请求头 - if (token) { - headers = { - ...headers, - Token: `${token}`, - } - } - - try { - const response = await fetch(`${API_BASE_URL}${url}`, { - ...options, - headers, - }) - - const data = await response.json() - - // 检查token是否过期(仅当有token时) - if (token && (data.code === 401 || data.code === 403)) { - // 清除token - localStorage.removeItem("token") - - // 暂时不重定向到登录页 - // if (typeof window !== "undefined") { - // window.location.href = "/login" - // } - - console.warn("登录已过期") - } - - return data - } catch (error) { - console.error("API请求错误:", error) - toast({ - variant: "destructive", - title: "请求失败", - description: error instanceof Error ? error.message : "网络错误,请稍后重试", - }) - throw error - } -} - -// 不需要认证的请求函数 -export async function publicFetch(url: string, options: RequestInit = {}) { - try { - const response = await fetch(`${API_BASE_URL}${url}`, options) - return await response.json() - } catch (error) { - console.error("API请求错误:", error) - toast({ - variant: "destructive", - title: "请求失败", - description: error instanceof Error ? error.message : "网络错误,请稍后重试", - }) - throw error - } -} diff --git a/Cunkebao/app/api/devices/route.ts b/Cunkebao/app/api/devices/route.ts deleted file mode 100644 index c7a1de4d..00000000 --- a/Cunkebao/app/api/devices/route.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { NextResponse } from "next/server" -import type { - CreateDeviceParams, - QueryDeviceParams, - Device, - ApiResponse, - DeviceStatus, - DeviceType, -} from "@/types/device" - -// 设备管理路由处理 -export async function POST(request: Request) { - try { - const body: CreateDeviceParams = await request.json() - - // TODO: 实现创建设备的具体逻辑 - const device: Device = { - id: "generated-id", - ...body, - status: DeviceStatus.OFFLINE, - lastOnlineTime: new Date().toISOString(), - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - } - - const response: ApiResponse = { - code: 0, - message: "创建成功", - data: device, - } - - 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: QueryDeviceParams = { - keyword: searchParams.get("keyword") || undefined, - status: (searchParams.get("status") as DeviceStatus) || undefined, - type: (searchParams.get("type") as DeviceType) || undefined, - tags: searchParams.get("tags") ? JSON.parse(searchParams.get("tags")!) : 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/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx b/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx deleted file mode 100644 index b37bd496..00000000 --- a/Cunkebao/app/api/docs/scenarios/[channel]/[id]/page.tsx +++ /dev/null @@ -1,377 +0,0 @@ -"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/docs/scenarios/page.tsx b/Cunkebao/app/api/docs/scenarios/page.tsx deleted file mode 100644 index f53009ef..00000000 --- a/Cunkebao/app/api/docs/scenarios/page.tsx +++ /dev/null @@ -1,405 +0,0 @@ -"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" - -const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'https://yishi.com' - -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) - - // 假设 fullUrl 和 apiKey 可通过 props 或接口获取,这里用演示值 - const [apiKey] = useState("naxf1-82h2f-vdwcm-rrhpm-q9hd1") - const [fullUrl] = useState("/v1/api/scenarios") - const testUrl = fullUrl.startsWith("http") ? fullUrl : `${API_BASE_URL}${fullUrl}` - - const copyToClipboard = (text: string, exampleId: string) => { - navigator.clipboard.writeText(text) - setCopiedExample(exampleId) - - toast({ - title: "已复制代码", - description: "代码示例已复制到剪贴板", - }) - - setTimeout(() => { - setCopiedExample(null) - }, 2000) - } - - return ( -
-
-
-
- -

计划接口文档

-
-
-
- -
- - - 接口说明 - 本接口用于将外部客户数据导入到存客宝计划。请使用 apiKey 进行身份认证,建议仅在服务端调用。 - - -
-
- -

- 支持多种编程语言和平台集成。接口地址和参数请参考下方说明。 -

-
-
-

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

-
-
-
-
- - - - 签名规则 - - -
-
- 签名参数: sign -
-
- 签名算法: 将所有请求参数(排除 sign 和 apiKey)按参数名升序排序,直接拼接参数值(不含等号和&),对该字符串进行 MD5 加密,得到中间串,再拼接 apiKey,最后再进行一次 MD5 加密,结果作为 sign 参数传递。 -
-
- 签名步骤: -
    -
  1. 去除 sign 和 apiKey 参数,将其余所有参数(如 name、phone、timestamp、source、remark、tags)按参数名升序排序
  2. -
  3. 直接拼接参数值,如 value1value2value3...
  4. -
  5. 对拼接后的字符串进行 MD5 加密,得到中间串
  6. -
  7. 将中间串与 apiKey 直接拼接
  8. -
  9. 对拼接后的字符串再进行一次 MD5 加密,结果即为 sign
  10. -
-
-
- 示例: -
-{`参数:
-  name=张三
-  phone=18888888888
-  timestamp=1700000000
-  apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1
-
-排序后拼接(排除apiKey,直接拼接值):
-  张三188888888881700000000
-
-第一步MD5:
-  md5(张三188888888881700000000) = 123456abcdef...
-
-拼接apiKey:
-  123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1
-
-第二步MD5:
-  sign=md5(123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1)`}
-                
-
-
注意:所有参数均需参与签名(除 sign 和 apiKey),且参数值需为原始值(不可 URL 编码)。
-
-
-
- - - - 接口地址 - - -
- {testUrl} -
-
-
必要参数: phone (电话), timestamp (时间戳), apiKey (接口密钥)
-
可选参数: name (姓名), source (来源), remark (备注), tags (标签)
-
请求方式: POSTGET
-
-
-
- - - - 请求参数 - - -
-
phone (string, 必填): 客户电话
-
timestamp (int, 必填): 当前时间戳,精确到秒(如 1700000000,建议用 Math.floor(Date.now() / 1000) 获取)
-
apiKey (string, 必填): 接口密钥
-
name (string, 可选): 客户姓名
-
source (string, 可选): 来源
-
remark (string, 可选): 备注
-
tags (string, 可选): 标签
-
-
-
- - - - 响应示例 - - -
-{`{
-  "code": 200,
-  "msg": "导入成功",
-  "data": {
-    "customerId": "123456"
-  }
-}`}
-            
-
-
- - - - - - 代码示例 - 以下是不同编程语言的接口调用示例 - - - - - cURL - Python - Node.js - PHP - Java - - -
{`curl -X POST 'http://yishi.com/v1/plan/api/scenariosz' \
-  -d "phone=18888888888" \
-  -d "timestamp=1700000000" \
-  -d "name=张三" \
-  -d "apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1" \
-  -d "sign=请用签名算法生成"`}
-
- -
{`import hashlib
-import time
-import requests
-
-def gen_sign(params, api_key):
-    data = {k: v for k, v in params.items() if k not in ('sign', 'apiKey')}
-    s = ''.join([str(data[k]) for k in sorted(data)])
-    first = hashlib.md5(s.encode('utf-8')).hexdigest()
-    return hashlib.md5((first + api_key).encode('utf-8')).hexdigest()
-
-api_key = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1'
-params = {
-    'phone': '18888888888',
-    'timestamp': int(time.time()),
-    'name': '张三',
-}
-params['apiKey'] = api_key
-params['sign'] = gen_sign(params, api_key)
-resp = requests.post('http://yishi.com/v1/plan/api/scenariosz', data=params)
-print(resp.json())`}
-
- -
{`const axios = require('axios');
-const crypto = require('crypto');
-
-function genSign(params, apiKey) {
-  const data = {...params};
-  delete data.sign;
-  delete data.apiKey;
-  const keys = Object.keys(data).sort();
-  let str = '';
-  keys.forEach(k => { str += data[k]; });
-  const first = crypto.createHash('md5').update(str).digest('hex');
-  return crypto.createHash('md5').update(first + apiKey).digest('hex');
-}
-
-const apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
-const params = {
-  phone: '18888888888',
-  timestamp: Math.floor(Date.now() / 1000),
-  name: '张三',
-};
-params.apiKey = apiKey;
-params.sign = genSign(params, apiKey);
-
-axios.post('http://yishi.com/v1/plan/api/scenariosz', params)
-  .then(res => console.log(res.data));`}
-
- -
{` '18888888888',
-    'timestamp' => time(),
-    'name' => '张三',
-    // 'source' => '', 'remark' => '', 'tags' => ''
-];
-$apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
-$params['apiKey'] = $apiKey;
-$params['sign'] = md5_sign($params, $apiKey);
-
-$url = '${API_BASE_URL}/v1/api/scenarios';
-
-$ch = curl_init();
-curl_setopt($ch, CURLOPT_URL, $url);
-curl_setopt($ch, CURLOPT_POST, 1);
-curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
-curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-$response = curl_exec($ch);
-curl_close($ch);
-echo $response;
-?>`}
-
- -
{`import java.security.MessageDigest;
-import java.util.*;
-import java.net.*;
-import java.io.*;
-
-public class ApiSignDemo {
-    public static String md5(String s) throws Exception {
-        MessageDigest md = MessageDigest.getInstance("MD5");
-        byte[] array = md.digest(s.getBytes("UTF-8"));
-        StringBuilder sb = new StringBuilder();
-        for (byte b : array) sb.append(String.format("%02x", b));
-        return sb.toString();
-    }
-    public static void main(String[] args) throws Exception {
-        Map params = new HashMap<>();
-        params.put("phone", "18888888888");
-        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
-        params.put("name", "张三");
-        // params.put("source", ""); params.put("remark", ""); params.put("tags", "");
-        String apiKey = "naxf1-82h2f-vdwcm-rrhpm-q9hd1";
-        // 排序并拼接
-        List keys = new ArrayList<>(params.keySet());
-        keys.remove("sign");
-        keys.remove("apiKey");
-        Collections.sort(keys);
-        StringBuilder sb = new StringBuilder();
-        for (String k : keys) {
-            sb.append(params.get(k));
-        }
-        String first = md5(sb.toString());
-        String sign = md5(first + apiKey);
-        params.put("apiKey", apiKey);
-        params.put("sign", sign);
-        // 发送POST请求
-        StringBuilder postData = new StringBuilder();
-        for (Map.Entry entry : params.entrySet()) {
-            if (postData.length() > 0) postData.append("&");
-            postData.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
-            postData.append("=");
-            postData.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
-        }
-        URL url = new URL("${API_BASE_URL}/v1/api/scenarios");
-        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-        conn.setRequestMethod("POST");
-        conn.setDoOutput(true);
-        OutputStream os = conn.getOutputStream();
-        os.write(postData.toString().getBytes("UTF-8"));
-        os.flush(); os.close();
-        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
-        String line; StringBuilder resp = new StringBuilder();
-        while ((line = in.readLine()) != null) resp.append(line);
-        in.close();
-        System.out.println(resp.toString());
-    }
-}`}
-
-
-
-
- -
-

集成指南

- - - - 集简云平台集成 - - -
    -
  1. 登录集简云平台
  2. -
  3. 导航至"应用集成" > "外部接口"
  4. -
  5. 选择"添加新接口",输入存客宝接口信息
  6. -
  7. 配置回调参数,将"X-API-KEY"设置为您的API密钥
  8. -
  9. - 设置接口URL为: - - {testUrl} - -
  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 deleted file mode 100644 index 7b5e7c0a..00000000 --- a/Cunkebao/app/api/scenarios/route.ts +++ /dev/null @@ -1,73 +0,0 @@ -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/Cunkebao/app/api/users/route.ts b/Cunkebao/app/api/users/route.ts deleted file mode 100644 index ace987c7..00000000 --- a/Cunkebao/app/api/users/route.ts +++ /dev/null @@ -1,272 +0,0 @@ -import { NextResponse } from "next/server" -import type { TrafficUser } from "@/types/traffic" - -// 中文名字生成器数据 -const familyNames = [ - "张", - "王", - "李", - "赵", - "陈", - "刘", - "杨", - "黄", - "周", - "吴", - "朱", - "孙", - "马", - "胡", - "郭", - "林", - "何", - "高", - "梁", - "郑", - "罗", - "宋", - "谢", - "唐", - "韩", - "曹", - "许", - "邓", - "萧", - "冯", -] -const givenNames1 = [ - "志", - "建", - "文", - "明", - "永", - "春", - "秀", - "金", - "水", - "玉", - "国", - "立", - "德", - "海", - "和", - "荣", - "伟", - "新", - "英", - "佳", -] -const givenNames2 = [ - "华", - "平", - "军", - "强", - "辉", - "敏", - "峰", - "磊", - "超", - "艳", - "娜", - "霞", - "燕", - "娟", - "静", - "丽", - "涛", - "洋", - "勇", - "龙", -] - -// 生成固定的用户数据池 -const userPool: TrafficUser[] = Array.from({ length: 1610 }, (_, i) => { - const familyName = familyNames[Math.floor(Math.random() * familyNames.length)] - const givenName1 = givenNames1[Math.floor(Math.random() * givenNames1.length)] - const givenName2 = givenNames2[Math.floor(Math.random() * givenNames2.length)] - const fullName = Math.random() > 0.5 ? familyName + givenName1 + givenName2 : familyName + givenName1 - - // 生成随机时间(在过去7天内) - const date = new Date() - date.setDate(date.getDate() - Math.floor(Math.random() * 7)) - - return { - id: `${Date.now()}-${i}`, - avatar: `/placeholder.svg?height=40&width=40&text=${fullName[0]}`, - nickname: fullName, - wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, - phone: `1${["3", "5", "7", "8", "9"][Math.floor(Math.random() * 5)]}${Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)).join("")}`, - region: [ - "广东深圳", - "浙江杭州", - "江苏苏州", - "北京", - "上海", - "四川成都", - "湖北武汉", - "福建厦门", - "山东青岛", - "河南郑州", - ][Math.floor(Math.random() * 10)], - note: [ - "咨询产品价格", - "对产品很感兴趣", - "准备购买", - "需要更多信息", - "想了解优惠活动", - "询问产品规格", - "要求产品demo", - "索要产品目录", - "询问售后服务", - "要求上门演示", - ][Math.floor(Math.random() * 10)], - status: ["pending", "added", "failed"][Math.floor(Math.random() * 3)] as TrafficUser["status"], - addTime: date.toISOString(), - source: ["抖音直播", "小红书", "微信朋友圈", "视频号", "公众号", "个人主页"][Math.floor(Math.random() * 6)], - assignedTo: "", - category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"], - tags: [], - } -}) - -// 计算今日新增数量 -const todayStart = new Date() -todayStart.setHours(0, 0, 0, 0) -const todayUsers = userPool.filter((user) => new Date(user.addTime) >= todayStart) - -// 生成微信好友数据池 -const generateWechatFriends = (wechatId: string, count: number) => { - return Array.from({ length: count }, (_, i) => { - const familyName = familyNames[Math.floor(Math.random() * familyNames.length)] - const givenName1 = givenNames1[Math.floor(Math.random() * givenNames1.length)] - const givenName2 = givenNames2[Math.floor(Math.random() * givenNames2.length)] - const fullName = Math.random() > 0.5 ? familyName + givenName1 + givenName2 : familyName + givenName1 - - // 生成随机时间(在过去30天内) - const date = new Date() - date.setDate(date.getDate() - Math.floor(Math.random() * 30)) - - return { - id: `wechat-${wechatId}-${i}`, - avatar: `/placeholder.svg?height=40&width=40&text=${fullName[0]}`, - nickname: fullName, - wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, - phone: `1${["3", "5", "7", "8", "9"][Math.floor(Math.random() * 5)]}${Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)).join("")}`, - region: [ - "广东深圳", - "浙江杭州", - "江苏苏州", - "北京", - "上海", - "四川成都", - "湖北武汉", - "福建厦门", - "山东青岛", - "河南郑州", - ][Math.floor(Math.random() * 10)], - note: [ - "咨询产品价格", - "对产品很感兴趣", - "准备购买", - "需要更多信息", - "想了解优惠活动", - "询问产品规格", - "要求产品demo", - "索要产品目录", - "询问售后服务", - "要求上门演示", - ][Math.floor(Math.random() * 10)], - status: ["pending", "added", "failed"][Math.floor(Math.random() * 3)] as TrafficUser["status"], - addTime: date.toISOString(), - source: ["抖音直播", "小红书", "微信朋友圈", "视频号", "公众号", "个人主页", "微信好友"][ - Math.floor(Math.random() * 7) - ], - assignedTo: "", - category: ["potential", "customer", "lost"][Math.floor(Math.random() * 3)] as TrafficUser["category"], - tags: [], - } - }) -} - -// 微信好友数据缓存 -const wechatFriendsCache = new Map() - -export async function GET(request: Request) { - const { searchParams } = new URL(request.url) - const page = Number.parseInt(searchParams.get("page") || "1") - const pageSize = Number.parseInt(searchParams.get("pageSize") || "10") - const search = searchParams.get("search") || "" - const category = searchParams.get("category") || "all" - const source = searchParams.get("source") || "all" - const status = searchParams.get("status") || "all" - const startDate = searchParams.get("startDate") - const endDate = searchParams.get("endDate") - const wechatSource = searchParams.get("wechatSource") || "" - - let filteredUsers = [...userPool] - - // 如果有微信来源参数,生成或获取微信好友数据 - if (wechatSource) { - if (!wechatFriendsCache.has(wechatSource)) { - // 生成150-300个随机好友 - const friendCount = Math.floor(Math.random() * (300 - 150)) + 150 - wechatFriendsCache.set(wechatSource, generateWechatFriends(wechatSource, friendCount)) - } - filteredUsers = wechatFriendsCache.get(wechatSource) || [] - } - - // 应用过滤条件 - filteredUsers = filteredUsers.filter((user) => { - const matchesSearch = search - ? user.nickname.toLowerCase().includes(search.toLowerCase()) || - user.wechatId.toLowerCase().includes(search.toLowerCase()) || - user.phone.includes(search) - : true - - const matchesCategory = category === "all" ? true : user.category === category - const matchesSource = source === "all" ? true : user.source === source - const matchesStatus = status === "all" ? true : user.status === status - - const matchesDate = - startDate && endDate - ? new Date(user.addTime) >= new Date(startDate) && new Date(user.addTime) <= new Date(endDate) - : true - - return matchesSearch && matchesCategory && matchesSource && matchesStatus && matchesDate - }) - - // 按添加时间倒序排序 - filteredUsers.sort((a, b) => new Date(b.addTime).getTime() - new Date(a.addTime).getTime()) - - // 计算分页 - const total = filteredUsers.length - const totalPages = Math.ceil(total / pageSize) - const start = (page - 1) * pageSize - const end = start + pageSize - const users = filteredUsers.slice(start, end) - - // 计算分类统计 - const categoryStats = { - potential: userPool.filter((user) => user.category === "potential").length, - customer: userPool.filter((user) => user.category === "customer").length, - lost: userPool.filter((user) => user.category === "lost").length, - } - - // 模拟网络延迟 - await new Promise((resolve) => setTimeout(resolve, 500)) - - return NextResponse.json({ - users, - pagination: { - total, - totalPages, - currentPage: page, - pageSize, - }, - stats: { - total: wechatSource ? filteredUsers.length : userPool.length, - todayNew: wechatSource ? Math.floor(filteredUsers.length * 0.1) : todayUsers.length, - categoryStats, - }, - }) -} diff --git a/Cunkebao/app/clientLayout.tsx b/Cunkebao/app/clientLayout.tsx deleted file mode 100644 index 892511aa..00000000 --- a/Cunkebao/app/clientLayout.tsx +++ /dev/null @@ -1 +0,0 @@ -// 这个文件不再需要,我们使用app/layout.tsx作为统一布局 diff --git a/Cunkebao/app/components-demo/loading.tsx b/Cunkebao/app/components-demo/loading.tsx deleted file mode 100644 index a69af020..00000000 --- a/Cunkebao/app/components-demo/loading.tsx +++ /dev/null @@ -1,34 +0,0 @@ -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 deleted file mode 100644 index 7d99363f..00000000 --- a/Cunkebao/app/components-demo/page.tsx +++ /dev/null @@ -1,493 +0,0 @@ -"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 - -
-
- - -
-
水平分隔符
- -
垂直分隔符
-
- 左侧内容 - - 右侧内容 -
-
-
-
- - {/* 表单组件 */} - - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
- -