超管后台 - 调整所有的fetch 请求为后封装的 apiRequest,实现cookie 跨域

This commit is contained in:
柳清爽
2025-04-28 15:39:05 +08:00
parent 0eb629df70
commit 35341335d3
15 changed files with 459 additions and 475 deletions

View File

@@ -7,6 +7,7 @@ import { toast } from "sonner"
import useAuthCheck from "@/hooks/useAuthCheck"
import { getAdminInfo, getGreeting } from "@/lib/utils"
import ClientOnly from "@/components/ClientOnly"
import { apiRequest } from '@/lib/api-utils'
interface DashboardStats {
companyCount: number
@@ -28,16 +29,14 @@ export default function DashboardPage() {
useAuthCheck()
useEffect(() => {
const fetchData = async () => {
setIsLoading(true)
const fetchDashboardData = async () => {
try {
// 获取统计信息
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/dashboard/base`)
const data = await response.json()
if (data.code === 200) {
setStats(data.data)
setIsLoading(true)
const result = await apiRequest('/dashboard/base')
if (result.code === 200 && result.data) {
setStats(result.data)
} else {
toast.error(data.msg || "获取统计信息失败")
toast.error(result.msg || "获取仪表盘数据失败")
}
// 获取用户信息
@@ -49,13 +48,14 @@ export default function DashboardPage() {
}
} catch (error) {
toast.error("网络错误,请稍后重试")
console.error("获取仪表盘数据失败:", error)
toast.error("网络错误,请稍后再试")
} finally {
setIsLoading(false)
}
}
fetchData()
fetchDashboardData()
}, [])
// 单独处理问候语,避免依赖问题

View File

@@ -13,6 +13,7 @@ import Link from "next/link"
import { toast, Toaster } from "sonner"
import Image from "next/image"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"
import { apiRequest } from "@/lib/api-utils"
// 为React.use添加类型声明
declare module 'react' {
@@ -69,30 +70,14 @@ export default function EditProjectPage({ params }: { params: { id: string } })
useEffect(() => {
const fetchProject = async () => {
setIsLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/detail/${id}`)
const result = await apiRequest(`/company/detail/${id}`)
// 检查响应状态
if (!response.ok) {
toast.error(`获取失败: ${response.status} ${response.statusText}`);
setIsLoading(false);
return;
}
// 检查内容类型是否为JSON
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
toast.error("服务器返回了非JSON格式的数据");
setIsLoading(false);
return;
}
const data = await response.json()
if (data.code === 200) {
setProject(data.data)
if (result.code === 200) {
setProject(result.data)
} else {
toast.error(data.msg || "获取项目信息失败")
toast.error(result.msg || "获取项目信息失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
@@ -106,54 +91,25 @@ export default function EditProjectPage({ params }: { params: { id: string } })
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (password && password !== confirmPassword) {
toast.error("两次输入的密码不一致")
return
}
setIsSubmitting(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/update`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: parseInt(id),
name: project?.name,
account: project?.account,
memo: project?.memo,
phone: project?.phone,
username: project?.username,
status: project?.status,
...(password && { password })
}),
const result = await apiRequest('/company/update', 'POST', {
id: id,
name: project?.name,
account: project?.account,
password: password,
memo: project?.memo,
phone: project?.phone,
username: project?.username,
status: project?.status.toString(),
})
// 检查响应状态
if (!response.ok) {
toast.error(`更新失败: ${response.status} ${response.statusText}`);
setIsSubmitting(false);
return;
}
// 检查内容类型是否为JSON
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
toast.error("服务器返回了非JSON格式的数据");
setIsSubmitting(false);
return;
}
const data = await response.json()
if (data.code === 200) {
toast.success("更新成功")
if (result.code === 200) {
toast.success("项目更新成功")
router.push("/dashboard/projects")
} else {
toast.error(data.msg)
toast.error(result.msg || "更新失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
@@ -177,33 +133,12 @@ export default function EditProjectPage({ params }: { params: { id: string } })
pollingCountRef.current = 0;
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/api/device/add`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
accountId: project.s2_accountId
}),
const result = await apiRequest('/v1/api/device/add', 'POST', {
accountId: project.s2_accountId
})
// 检查响应状态
if (!response.ok) {
toast.error(`请求失败: ${response.status} ${response.statusText}`);
return;
}
// 检查内容类型是否为JSON
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
toast.error("服务器返回了非JSON格式的数据");
return;
}
const data = await response.json()
if (data.code === 200 && data.data?.qrCode) {
setQrCodeData(data.data.qrCode)
if (result.code === 200 && result.data?.qrCode) {
setQrCodeData(result.data.qrCode)
setIsModalOpen(true)
// 五秒后开始轮询
@@ -211,7 +146,7 @@ export default function EditProjectPage({ params }: { params: { id: string } })
startPolling();
}, 5000);
} else {
toast.error(data.msg || "获取设备二维码失败")
toast.error(result.msg || "获取设备二维码失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
@@ -248,33 +183,11 @@ export default function EditProjectPage({ params }: { params: { id: string } })
}
try {
const accountId = project.s2_accountId;
// 通过URL参数传递accountId
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/devices/add-results?accountId=${accountId}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const result = await apiRequest(`/devices/add-results?accountId=${project.s2_accountId}`)
// 检查响应状态和内容类型
if (!response.ok) {
console.error("轮询请求失败:", response.status, response.statusText);
return;
}
// 检查内容类型是否为JSON
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
console.error("轮询请求返回的不是JSON格式:", contentType);
return;
}
const data = await response.json();
if (data.code === 200) {
if (result.code === 200) {
// 检查是否最后一次轮询且设备未添加
if (pollingCountRef.current >= MAX_POLLING_COUNT && !data.added) {
if (pollingCountRef.current >= MAX_POLLING_COUNT && !result.data.added) {
setPollingStatus("error");
setIsQrCodeBroken(true);
stopPolling();
@@ -282,9 +195,9 @@ export default function EditProjectPage({ params }: { params: { id: string } })
}
// 检查设备是否已添加成功
if (data.added) {
if (result.data.added) {
setPollingStatus("success");
setAddedDevice(data.device);
setAddedDevice(result.data.device);
stopPolling();
// 刷新设备列表
@@ -293,7 +206,7 @@ export default function EditProjectPage({ params }: { params: { id: string } })
}
} else {
// 请求失败但继续轮询
console.error("轮询请求失败:", data.msg);
console.error("轮询请求失败:", result.msg);
}
} catch (error) {
console.error("轮询请求出错:", error);
@@ -304,27 +217,12 @@ export default function EditProjectPage({ params }: { params: { id: string } })
// 刷新项目数据的方法
const refreshProjectData = async () => {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/detail/${id}`)
const result = await apiRequest(`/company/detail/${id}`)
// 检查响应状态
if (!response.ok) {
toast.error(`刷新失败: ${response.status} ${response.statusText}`);
return;
}
// 检查内容类型是否为JSON
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
toast.error("服务器返回了非JSON格式的数据");
return;
}
const data = await response.json()
if (data.code === 200) {
setProject(data.data)
if (result.code === 200) {
setProject(result.data)
} else {
toast.error(data.msg || "刷新项目信息失败")
toast.error(result.msg || "刷新项目信息失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")

View File

@@ -13,6 +13,7 @@ import { toast } from "sonner"
import { use } from "react"
import Image from "next/image"
import { Badge } from "@/components/ui/badge"
import { apiRequest } from '@/lib/api-utils'
interface ProjectProfile {
id: number
@@ -69,75 +70,68 @@ export default function ProjectDetailPage({ params }: ProjectDetailPageProps) {
const [subUsers, setSubUsers] = useState<SubUser[]>([])
const [activeTab, setActiveTab] = useState("overview")
useEffect(() => {
const fetchProjectProfile = async () => {
const fetchProject = async () => {
try {
setIsLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/profile/${id}`)
const data = await response.json()
if (data.code === 200) {
setProfile(data.data)
} else {
toast.error(data.msg || "获取项目信息失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
} finally {
setIsLoading(false)
const result = await apiRequest(`/company/profile/${id}`)
if (result.code === 200 && result.data) {
setProfile(result.data)
} else {
toast.error(result.msg || "获取项目信息失败")
}
} catch (error) {
console.error("获取项目信息失败:", error)
toast.error("网络错误,请稍后再试")
} finally {
setIsLoading(false)
}
}
fetchProjectProfile()
useEffect(() => {
fetchProject()
}, [id])
useEffect(() => {
const fetchDevices = async () => {
if (activeTab === "devices") {
setIsDevicesLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/devices?companyId=${id}`)
const data = await response.json()
if (data.code === 200) {
setDevices(data.data)
} else {
toast.error(data.msg || "获取设备列表失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
} finally {
setIsDevicesLoading(false)
}
const fetchDevices = async () => {
try {
setIsDevicesLoading(true)
const result = await apiRequest(`/company/devices?companyId=${id}`)
if (result.code === 200 && result.data) {
setDevices(result.data)
} else {
toast.error(result.msg || "获取设备列表失败")
}
} catch (error) {
console.error("获取设备列表失败:", error)
toast.error("网络错误,请稍后再试")
} finally {
setIsDevicesLoading(false)
}
}
useEffect(() => {
fetchDevices()
}, [activeTab, id])
useEffect(() => {
const fetchSubUsers = async () => {
if (activeTab === "accounts") {
setIsSubUsersLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/subusers?companyId=${id}`)
const data = await response.json()
if (data.code === 200) {
setSubUsers(data.data)
} else {
toast.error(data.msg || "获取子账号列表失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
} finally {
setIsSubUsersLoading(false)
setIsSubUsersLoading(true)
try {
const result = await apiRequest(`/company/subusers?companyId=${id}`)
if (result.code === 200) {
setSubUsers(result.data)
} else {
toast.error(result.msg || "获取子账号列表失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")
} finally {
setIsSubUsersLoading(false)
}
}
fetchSubUsers()
}, [activeTab, id])
}, [id])
if (isLoading) {
return <div className="flex items-center justify-center min-h-screen">...</div>

View File

@@ -12,6 +12,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { ArrowLeft } from "lucide-react"
import Link from "next/link"
import { toast, Toaster } from "sonner"
import { apiRequest } from "@/lib/api-utils"
export default function NewProjectPage() {
const router = useRouter()
@@ -53,29 +54,21 @@ export default function NewProjectPage() {
setIsSubmitting(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/add`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: formData.name,
account: formData.account,
password: formData.password,
memo: formData.description,
phone: formData.phone,
username: formData.nickname,
status: parseInt(formData.status),
}),
const result = await apiRequest('/company/add', 'POST', {
name: formData.name,
account: formData.account,
password: formData.password,
memo: formData.description,
phone: formData.phone,
username: formData.nickname,
status: parseInt(formData.status),
})
const data = await response.json()
if (data.code === 200) {
toast.success("创建成功")
if (result.code === 200) {
toast.success("项目创建成功")
router.push("/dashboard/projects")
} else {
toast.error(data.msg)
toast.error(result.msg || "创建失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")

View File

@@ -19,6 +19,7 @@ import {
import { Badge } from "@/components/ui/badge"
import { PaginationControls } from "@/components/ui/pagination-controls"
import { useTabContext } from "@/app/dashboard/layout"
import { apiRequest } from "@/lib/api-utils"
interface Project {
id: number
@@ -73,15 +74,14 @@ export default function ProjectsPage() {
const fetchProjects = async () => {
setIsLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/list?page=${currentPage}&limit=${pageSize}`)
const data = await response.json()
const result = await apiRequest(`/company/list?page=${currentPage}&limit=${pageSize}`)
if (data.code === 200) {
setProjects(data.data.list)
setTotalItems(data.data.total)
setTotalPages(Math.ceil(data.data.total / pageSize))
if (result.code === 200) {
setProjects(result.data.list)
setTotalItems(result.data.total)
setTotalPages(Math.ceil(result.data.total / pageSize))
} else {
toast.error(data.msg || "获取项目列表失败")
toast.error(result.msg || "获取项目列表失败")
setProjects([])
setTotalItems(0)
setTotalPages(0)
@@ -124,33 +124,24 @@ export default function ProjectsPage() {
setIsDeleting(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/delete`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: deletingProjectId
}),
const result = await apiRequest('/company/delete', 'POST', {
id: deletingProjectId
})
const data = await response.json()
if (data.code === 200) {
if (result.code === 200) {
toast.success("删除成功")
// Fetch projects again after delete
const fetchProjects = async () => {
setIsLoading(true)
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/list?page=${currentPage}&limit=${pageSize}`)
const data = await response.json()
if (data.code === 200) {
setProjects(data.data.list)
setTotalItems(data.data.total)
setTotalPages(Math.ceil(data.data.total / pageSize))
if (currentPage > Math.ceil(data.data.total / pageSize) && Math.ceil(data.data.total / pageSize) > 0) {
setCurrentPage(Math.ceil(data.data.total / pageSize));
const result = await apiRequest(`/company/list?page=${currentPage}&limit=${pageSize}`)
if (result.code === 200) {
setProjects(result.data.list)
setTotalItems(result.data.total)
setTotalPages(Math.ceil(result.data.total / pageSize))
if (currentPage > Math.ceil(result.data.total / pageSize) && Math.ceil(result.data.total / pageSize) > 0) {
setCurrentPage(Math.ceil(result.data.total / pageSize));
}
} else {
setProjects([]); setTotalItems(0); setTotalPages(0);
@@ -160,7 +151,7 @@ export default function ProjectsPage() {
}
fetchProjects();
} else {
toast.error(data.msg || "删除失败")
toast.error(result.msg || "删除失败")
}
} catch (error) {
toast.error("网络错误,请稍后重试")