存客宝 React
This commit is contained in:
69
Cunkebao/lib/api/auth.ts
Normal file
69
Cunkebao/lib/api/auth.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
125
Cunkebao/lib/api/content.ts
Normal file
125
Cunkebao/lib/api/content.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { ContentLibrary, ContentItem } from "@/types/content-library"
|
||||
|
||||
const API_BASE = "/api/content"
|
||||
|
||||
// 内容库API
|
||||
export const contentApi = {
|
||||
// 创建内容库
|
||||
async createLibrary(data: Partial<ContentLibrary>): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容库列表
|
||||
async getLibraries(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<ContentLibrary>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/libraries?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容库详情
|
||||
async getLibraryById(id: string): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新内容库
|
||||
async updateLibrary(id: string, data: Partial<ContentLibrary>): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除内容库
|
||||
async deleteLibrary(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 创建内容项
|
||||
async createItem(libraryId: string, data: Partial<ContentItem>): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容项列表
|
||||
async getItems(
|
||||
libraryId: string,
|
||||
params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
},
|
||||
): Promise<ApiResponse<PaginatedResponse<ContentItem>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容项详情
|
||||
async getItemById(libraryId: string, itemId: string): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新内容项
|
||||
async updateItem(libraryId: string, itemId: string, data: Partial<ContentItem>): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除内容项
|
||||
async deleteItem(libraryId: string, itemId: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
}
|
||||
|
||||
131
Cunkebao/lib/api/devices.ts
Normal file
131
Cunkebao/lib/api/devices.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import type {
|
||||
ApiResponse,
|
||||
Device,
|
||||
DeviceStats,
|
||||
DeviceTaskRecord,
|
||||
PaginatedResponse,
|
||||
QueryDeviceParams,
|
||||
CreateDeviceParams,
|
||||
UpdateDeviceParams,
|
||||
DeviceStatus,
|
||||
} from "@/types/device"
|
||||
|
||||
const API_BASE = "/api/devices"
|
||||
|
||||
// 设备管理API
|
||||
export const deviceApi = {
|
||||
// 创建设备
|
||||
async create(params: CreateDeviceParams): Promise<ApiResponse<Device>> {
|
||||
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<ApiResponse<Device>> {
|
||||
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<ApiResponse<Device>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 查询设备列表
|
||||
async query(params: QueryDeviceParams): Promise<ApiResponse<PaginatedResponse<Device>>> {
|
||||
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<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 重启设备
|
||||
async restart(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/restart`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 解绑设备
|
||||
async unbind(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/unbind`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取设备统计数据
|
||||
async getStats(id: string): Promise<ApiResponse<DeviceStats>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/stats`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取设备任务记录
|
||||
async getTaskRecords(id: string, page = 1, pageSize = 20): Promise<ApiResponse<PaginatedResponse<DeviceTaskRecord>>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/tasks?page=${page}&pageSize=${pageSize}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 批量更新设备标签
|
||||
async updateTags(ids: string[], tags: string[]): Promise<ApiResponse<void>> {
|
||||
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<Blob> {
|
||||
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<ApiResponse<Record<string, DeviceStatus>>> {
|
||||
const response = await fetch(`${API_BASE}/status`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ deviceIds: ids }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
}
|
||||
|
||||
8
Cunkebao/lib/api/index.ts
Normal file
8
Cunkebao/lib/api/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// 导出所有API客户端,方便统一导入
|
||||
export * from "./scenarios"
|
||||
export * from "./devices"
|
||||
export * from "./users"
|
||||
export * from "./content"
|
||||
export * from "./traffic"
|
||||
export * from "./workspace"
|
||||
|
||||
112
Cunkebao/lib/api/scenarios.ts
Normal file
112
Cunkebao/lib/api/scenarios.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
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<ApiResponse<ScenarioBase>> {
|
||||
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<ApiResponse<ScenarioBase>> {
|
||||
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<ApiResponse<ScenarioBase>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 查询场景列表
|
||||
async query(params: QueryScenarioParams): Promise<ApiResponse<PaginatedResponse<ScenarioBase>>> {
|
||||
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<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 启动场景
|
||||
async start(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/start`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 暂停场景
|
||||
async pause(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/pause`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取场景统计数据
|
||||
async getStats(id: string): Promise<ApiResponse<ScenarioStats>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/stats`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取获客记录
|
||||
async getRecords(id: string, page = 1, pageSize = 20): Promise<ApiResponse<PaginatedResponse<AcquisitionRecord>>> {
|
||||
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<Blob> {
|
||||
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<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/tags`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ customerIds, tags }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
}
|
||||
|
||||
122
Cunkebao/lib/api/traffic.ts
Normal file
122
Cunkebao/lib/api/traffic.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { TrafficPool, TrafficDistribution } from "@/types/traffic"
|
||||
|
||||
const API_BASE = "/api/traffic"
|
||||
|
||||
// 流量池API
|
||||
export const trafficApi = {
|
||||
// 创建流量池
|
||||
async createPool(data: Partial<TrafficPool>): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量池列表
|
||||
async getPools(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<TrafficPool>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/pools?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量池详情
|
||||
async getPoolById(id: string): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新流量池
|
||||
async updatePool(id: string, data: Partial<TrafficPool>): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除流量池
|
||||
async deletePool(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 创建流量分配
|
||||
async createDistribution(data: Partial<TrafficDistribution>): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量分配列表
|
||||
async getDistributions(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
poolId?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<TrafficDistribution>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/distributions?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量分配详情
|
||||
async getDistributionById(id: string): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新流量分配
|
||||
async updateDistribution(id: string, data: Partial<TrafficDistribution>): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除流量分配
|
||||
async deleteDistribution(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
}
|
||||
|
||||
91
Cunkebao/lib/api/users.ts
Normal file
91
Cunkebao/lib/api/users.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { TrafficUser } from "@/types/traffic"
|
||||
import type { ApiResponse } from "@/types/common"
|
||||
|
||||
const API_BASE = "/api/users"
|
||||
|
||||
// 用户管理API
|
||||
export const userApi = {
|
||||
// 查询用户列表
|
||||
async query(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
category?: string
|
||||
source?: string
|
||||
status?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
wechatSource?: string
|
||||
}): Promise<{
|
||||
users: TrafficUser[]
|
||||
pagination: {
|
||||
total: number
|
||||
totalPages: number
|
||||
currentPage: number
|
||||
pageSize: number
|
||||
}
|
||||
stats: {
|
||||
total: number
|
||||
todayNew: number
|
||||
categoryStats: {
|
||||
potential: number
|
||||
customer: number
|
||||
lost: number
|
||||
}
|
||||
}
|
||||
}> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取用户详情
|
||||
async getById(id: string): Promise<ApiResponse<TrafficUser>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
async update(id: string, data: Partial<TrafficUser>): Promise<ApiResponse<TrafficUser>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 批量更新用户标签
|
||||
async updateTags(ids: string[], tags: string[]): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/tags`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ userIds: ids, tags }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 批量导出用户数据
|
||||
async exportUsers(ids: string[]): Promise<Blob> {
|
||||
const response = await fetch(`${API_BASE}/export`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ userIds: ids }),
|
||||
})
|
||||
return response.blob()
|
||||
},
|
||||
}
|
||||
|
||||
319
Cunkebao/lib/api/workspace.ts
Normal file
319
Cunkebao/lib/api/workspace.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { MomentsSync, GroupSync, GroupPush, AutoLike, AutoGroup } from "@/types/workspace"
|
||||
|
||||
const API_BASE = "/api/workspace"
|
||||
|
||||
// 工作区API
|
||||
export const workspaceApi = {
|
||||
// 朋友圈同步API
|
||||
moments: {
|
||||
// 创建朋友圈同步任务
|
||||
async create(data: Partial<MomentsSync>): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取朋友圈同步任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<MomentsSync>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/moments-sync?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取朋友圈同步任务详情
|
||||
async getById(id: string): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新朋友圈同步任务
|
||||
async update(id: string, data: Partial<MomentsSync>): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除朋友圈同步任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 启动朋友圈同步任务
|
||||
async start(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}/start`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 暂停朋友圈同步任务
|
||||
async pause(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}/pause`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 群同步API
|
||||
groupSync: {
|
||||
// 创建群同步任务
|
||||
async create(data: Partial<GroupSync>): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群同步任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<GroupSync>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/group-sync?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群同步任务详情
|
||||
async getById(id: string): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新群同步任务
|
||||
async update(id: string, data: Partial<GroupSync>): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除群同步任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 群发API
|
||||
groupPush: {
|
||||
// 创建群发任务
|
||||
async create(data: Partial<GroupPush>): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群发任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<GroupPush>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/group-push?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群发任务详情
|
||||
async getById(id: string): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新群发任务
|
||||
async update(id: string, data: Partial<GroupPush>): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除群发任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 自动点赞API
|
||||
autoLike: {
|
||||
// 创建自动点赞任务
|
||||
async create(data: Partial<AutoLike>): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动点赞任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<AutoLike>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/auto-like?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动点赞任务详情
|
||||
async getById(id: string): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新自动点赞任务
|
||||
async update(id: string, data: Partial<AutoLike>): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除自动点赞任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 自动建群API
|
||||
autoGroup: {
|
||||
// 创建自动建群任务
|
||||
async create(data: Partial<AutoGroup>): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动建群任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<AutoGroup>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/auto-group?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动建群任务详情
|
||||
async getById(id: string): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新自动建群任务
|
||||
async update(id: string, data: Partial<AutoGroup>): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除自动建群任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
60
Cunkebao/lib/tutorials.ts
Normal file
60
Cunkebao/lib/tutorials.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { PageTutorial, TutorialVideo } from "@/types/tutorial"
|
||||
|
||||
// 模拟后台配置的教程视频数据
|
||||
export const tutorialConfig: PageTutorial[] = [
|
||||
{
|
||||
pageId: "home",
|
||||
videos: [
|
||||
{
|
||||
id: "home-intro",
|
||||
title: "存客宝使用介绍",
|
||||
description: "了解存客宝的主要功能和基本操作流程",
|
||||
url: "/videos/home-intro.mp4",
|
||||
thumbnailUrl: "/placeholder.svg?height=360&width=640",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pageId: "scenarios/xiaohongshu",
|
||||
videos: [
|
||||
{
|
||||
id: "xiaohongshu-intro",
|
||||
title: "小红书获客功能介绍",
|
||||
description: "学习如何使用小红书场景获取精准客户",
|
||||
url: "/videos/xiaohongshu-intro.mp4",
|
||||
thumbnailUrl: "/placeholder.svg?height=360&width=640",
|
||||
},
|
||||
{
|
||||
id: "xiaohongshu-setup",
|
||||
title: "小红书获客配置教程",
|
||||
description: "详细了解小红书获客的各项配置选项",
|
||||
url: "/videos/xiaohongshu-setup.mp4",
|
||||
thumbnailUrl: "/placeholder.svg?height=360&width=640",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pageId: "scenarios/douyin",
|
||||
videos: [
|
||||
{
|
||||
id: "douyin-intro",
|
||||
title: "抖音获客功能介绍",
|
||||
description: "学习如何使用抖音场景获取精准客户",
|
||||
url: "/videos/douyin-intro.mp4",
|
||||
thumbnailUrl: "/placeholder.svg?height=360&width=640",
|
||||
},
|
||||
],
|
||||
},
|
||||
// ... 其他页面的视频配置
|
||||
]
|
||||
|
||||
export function getPageTutorials(path: string): TutorialVideo[] {
|
||||
// 移除开头的斜杠并标准化路径
|
||||
const normalizedPath = path.replace(/^\/+/, "")
|
||||
|
||||
// 查找匹配的页面教程
|
||||
const pageTutorial = tutorialConfig.find((config) => normalizedPath.startsWith(config.pageId))
|
||||
|
||||
return pageTutorial?.videos || []
|
||||
}
|
||||
|
||||
7
Cunkebao/lib/utils.ts
Normal file
7
Cunkebao/lib/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user