Jelajahi Sumber

跟新clientRequest

丁烨烨 4 bulan lalu
induk
melakukan
5dacb123c2

+ 3 - 2
.env.production

@@ -1,4 +1,5 @@
-NEXT_PUBLIC_API_BASE=http://localhost:8040
-NEXT_PUBLIC_BASE_URL=http://localhost:8040
+//NEXT_PUBLIC_API_BASE=http://localhost:8040
+NEXT_PUBLIC_API_BASE=http://47.107.107.47:8040
+NEXT_PUBLIC_BASE_URL=http://47.107.107.47:8040
 NEXT_PUBLIC_REMOTE_BASE_URL=http://47.107.107.47:8040
 NEXT_PUBLIC_BASE_WS_URL = 'ws://47.107.107.47:8081/ws'

+ 143 - 95
src/components/products/ProductGridClient.tsx

@@ -1,12 +1,12 @@
 "use client"
 
-import React, { useState, useEffect } from "react"
+import React, { useState } from "react"
 import Image from "next/image"
 import { Button, Form, Input, message, Modal, Pagination, ConfigProvider } from "antd"
 import Link from "next/link";
-import { clientPost } from "@/utils/request";
-
-// 留言表单数据接口(移除quantity字段)
+import { clientPost } from "@/utils/clientRequest";
+import type { ApiResponse } from '@/utils/clientRequest'
+// 留言表单数据接口
 interface QuoteFormValues {
     name: string;
     phone: string;
@@ -22,7 +22,7 @@ interface SaveQuotePayload {
     phone: string;
 }
 
-// 补充 ProductItem 接口定义(避免类型报错)
+// ProductItem 接口定义
 interface ProductItem {
     productId: string;
     productName: string;
@@ -36,7 +36,8 @@ interface ProductGridProps {
 }
 
 export default function ProductGrid({ products }: ProductGridProps) {
-    const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL as string
+    // 修复:增加空值判断,避免环境变量未定义报错
+    const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || ''
 
     // 分页控制
     const [currentPage, setCurrentPage] = useState(1)
@@ -46,29 +47,27 @@ export default function ProductGrid({ products }: ProductGridProps) {
     const [isModalOpen, setIsModalOpen] = useState(false)
     const [currentProduct, setCurrentProduct] = useState<ProductItem | null>(null)
     const [form] = Form.useForm()
-    // 添加加载状态,防止重复提交
     const [submitting, setSubmitting] = useState(false)
 
+    // 创建 message 实例(AntD v5 必须)
     const [messageApi, contextHolder] = message.useMessage()
 
-    // 当前页数据
-    const startIndex = (currentPage - 1) * pageSize
+    // 当前页数据(增加边界保护)
+    const startIndex = Math.max(0, (currentPage - 1) * pageSize)
     const currentProducts = products.slice(startIndex, startIndex + pageSize)
 
-    // 打开弹窗并记录当前产品(移除quantity初始化)
+    // 打开弹窗
     const showModal = (product: ProductItem) => {
         setCurrentProduct(product)
         setIsModalOpen(true)
-        // 延迟设置表单值,确保Modal渲染完成后再初始化Form
-        setTimeout(() => {
-            form.setFieldsValue({
-                productId: product.productId,
-                productName: product.productName
-            })
-        }, 0)
+        // 立即设置表单值(无需 setTimeout)
+        form.setFieldsValue({
+            productId: product.productId,
+            productName: product.productName
+        })
     }
 
-    // 关闭弹窗(优化:增加延迟,确保提示显示完成)
+    // 关闭弹窗
     const handleCancel = () => {
         setIsModalOpen(false)
         setCurrentProduct(null)
@@ -76,101 +75,129 @@ export default function ProductGrid({ products }: ProductGridProps) {
         setSubmitting(false)
     }
 
-    // 提交报价表单(完善接口调用逻辑
+    // 提交报价表单(核心修复
     const handleSubmit = async (values: QuoteFormValues) => {
         try {
-            // 1. 设置提交中状态,禁用按钮
             setSubmitting(true)
 
-            const response = await clientPost('/webSite/save', {
-                content: values.message,
-                deviceName: values.productName,
+            // 直接使用 clientPost 发送 POST 请求
+            const response: ApiResponse = await clientPost('/webSite/save', {
                 name: values.name,
-                phone: values.phone
+                phone: values.phone,
+                content: values.message,
+                deviceName: values.productName
             }, {
-                timeout: 15000,
+                timeout: 20000, // 自定义超时
                 headers: {
-                    'X-Client-Type': 'browser'
+                    'X-Custom-Header': 'custom-value'
                 }
             })
 
-            // 关键修复2:使用独立的 messageApi 显示提示
+            // 显示成功提示并延迟关闭弹窗
             messageApi.success("报价请求提交成功!我们会尽快联系你")
             handleCancel()
         } catch (error) {
-            // 异常处理也使用 messageApi
-            const errorMsg = error instanceof Error ? error.message : "提交失败,请稍后重试"
+            // 更友好的错误提示
+            let errorMsg = "提交失败,请稍后重试"
+            if (error instanceof Error) {
+                if (error.message.includes('Network')) {
+                    errorMsg = "网络连接失败,请检查您的网络"
+                } else if (error.message.includes('timeout')) {
+                    errorMsg = "请求超时,请稍后重试"
+                } else {
+                    errorMsg = error.message
+                }
+            }
             messageApi.error(errorMsg)
+            console.error("提交报价失败:", error)
         } finally {
-            // 只有失败时才在这里重置状态(成功时在提示关闭后重置)
-            if (submitting) {
-                setSubmitting(false)
-            }
+            // 无论成功失败都重置提交状态
+            setSubmitting(false)
+        }
+    }
+
+    // 处理图片路径(增加容错)
+    const getProductImageUrl = (product: ProductItem) => {
+        if (product.productUrl) {
+            return `${BASE_URL}${product.productUrl}`
         }
+        return "/assets/productions/2.png"
     }
 
     return (
-        // 关键修复3:添加 ConfigProvider 包裹组件,确保 AntD 上下文正确
         <ConfigProvider>
-            {/* 必须添加 contextHolder 到组件中,否则 message 无法显示 */}
-            {contextHolder}
+            {contextHolder} {/* 必须挂载 message 上下文 */}
 
             <div className="flex flex-col gap-6">
-                {/* 产品网格 */}
-                <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
-                    {currentProducts.map((product) => (
-                        <div
-                            key={product.productId}
-                            className="bg-white rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300 hover:scale-[1.02]"
-                        >
-                            <div className="p-2">
-                                <Image
-                                    className={"h-50 object-contain"}
-                                    src={product.productUrl ? BASE_URL + product.productUrl : "/assets/productions/2.png"}
-                                    alt={product.productName}
-                                    width={1000}
-                                    height={1000}
-                                    priority={false}
-                                />
-                            </div>
-                            <div className="p-4 sm:py-6 text-center">
-                                <h3 className="font-bold text-lg mb-1">{product.productName}</h3>
-                                <p className="text-sm text-gray-500 mb-2 line-clamp-1">{product.productIntroduction}</p>
-                                <p className="text-sm mb-3">{product.productModel}</p>
-
-                                <div className="w-full flex justify-center">
-                                    <button
-                                        onClick={() => showModal(product)}
-                                        className="bg-red-600 text-white !text-white px-4 !px-4 py-1.5 !py-1.5 rounded hover:bg-red-700 transition-colors duration-200"
-                                    >
-                                        获取报价
-                                    </button>
-                                    <Link
-                                        href={`/products/${product.productId}`}
-                                        className="bg-blue-600 text-white !text-white px-4 !px-4 py-1.5 !py-1.5 rounded hover:bg-blue-700 transition-colors duration-200 ml-2"
-                                    >
-                                        了解详情
-                                    </Link>
+                {/* 产品网格(增加空数据处理) */}
+                {currentProducts.length === 0 ? (
+                    <div className="text-center py-10">暂无产品数据</div>
+                ) : (
+                    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
+                        {currentProducts.map((product) => (
+                            <div
+                                key={product.productId}
+                                className="bg-white rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300 hover:scale-[1.02]"
+                            >
+                                <div className="p-2 aspect-square relative">
+                                    <Image
+                                        className="h-full w-full object-contain"
+                                        src={getProductImageUrl(product)}
+                                        alt={product.productName || '产品图片'}
+                                        fill // Next.js 13+ 推荐用法
+                                        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
+                                        priority={false}
+                                        // 图片加载失败兜底
+                                        onError={(e) => {
+                                            e.currentTarget.src = "/assets/productions/2.png"
+                                        }}
+                                    />
+                                </div>
+                                <div className="p-4 sm:py-6 text-center">
+                                    <h3 className="font-bold text-lg mb-1">{product.productName}</h3>
+                                    <p className="text-sm text-gray-500 mb-2 line-clamp-1">
+                                        {product.productIntroduction || '暂无介绍'}
+                                    </p>
+                                    <p className="text-sm mb-3">{product.productModel || '暂无型号'}</p>
+
+                                    <div className="w-full flex justify-center gap-2">
+                                        <button
+                                            onClick={() => showModal(product)}
+                                            className="bg-red-600 text-white px-4 py-1.5 rounded hover:bg-red-700 transition-colors duration-200"
+                                            disabled={submitting}
+                                        >
+                                            获取报价
+                                        </button>
+                                        <Link
+                                            href={`/products/${product.productId}`}
+                                            className="bg-blue-600 text-white px-4 py-1.5 rounded hover:bg-blue-700 transition-colors duration-200"
+                                        >
+                                            了解详情
+                                        </Link>
+                                    </div>
                                 </div>
                             </div>
-                        </div>
-                    ))}
-                </div>
-
-                {/* 分页器 */}
-                <div className="flex justify-center">
-                    <Pagination
-                        current={currentPage}
-                        pageSize={pageSize}
-                        total={products.length}
-                        onChange={(page) => setCurrentPage(page)}
-                        showSizeChanger={false}
-                    />
-                </div>
-
-                {/* 报价弹窗(移除采购数量相关内容) */}
+                        ))}
+                    </div>
+                )}
+
+                {/* 分页器(只有数据大于分页大小时显示) */}
+                {products.length > pageSize && (
+                    <div className="flex justify-center">
+                        <Pagination
+                            current={currentPage}
+                            pageSize={pageSize}
+                            total={products.length}
+                            onChange={(page) => setCurrentPage(page)}
+                            showSizeChanger={false}
+                            disabled={submitting}
+                        />
+                    </div>
+                )}
+
+                {/* 报价弹窗 */}
                 <Modal
-                    title={`获取 "${currentProduct?.productName}" 报价`}
+                    title={`获取 "${currentProduct?.productName || '产品'}" 报价`}
                     open={isModalOpen}
                     onCancel={handleCancel}
                     footer={null}
@@ -181,6 +208,11 @@ export default function ProductGrid({ products }: ProductGridProps) {
                         form={form}
                         layout="vertical"
                         onFinish={handleSubmit}
+                        initialValues={{
+                            name: '',
+                            phone: '',
+                            message: ''
+                        }}
                     >
                         {/* 隐藏字段 */}
                         <Form.Item name="productId" hidden>
@@ -194,21 +226,32 @@ export default function ProductGrid({ products }: ProductGridProps) {
                         <Form.Item
                             name="name"
                             label="联系人姓名"
-                            rules={[{ required: true, message: "请输入您的姓名" }]}
+                            rules={[{
+                                required: true,
+                                message: "请输入您的姓名",
+                                whitespace: true // 禁止纯空格
+                            }]}
                         >
-                            <Input placeholder="请输入您的姓名" />
+                            <Input placeholder="请输入您的姓名" disabled={submitting} />
                         </Form.Item>
 
-                        {/* 联系电话 */}
+                        {/* 联系电话(优化正则) */}
                         <Form.Item
                             name="phone"
                             label="联系电话"
                             rules={[
                                 { required: true, message: "请输入您的联系电话" },
-                                { pattern: /^1[3-9]\d{9}$/, message: "请输入有效的手机号码" }
+                                {
+                                    pattern: /^1[3-9]\d{9}$/,
+                                    message: "请输入有效的11位手机号码"
+                                }
                             ]}
                         >
-                            <Input placeholder="请输入您的手机号码" />
+                            <Input
+                                placeholder="请输入您的手机号码"
+                                maxLength={11}
+                                disabled={submitting}
+                            />
                         </Form.Item>
 
                         {/* 留言/备注 */}
@@ -219,13 +262,18 @@ export default function ProductGrid({ products }: ProductGridProps) {
                             <Input.TextArea
                                 rows={4}
                                 placeholder="请输入您的其他需求或备注信息(选填)"
+                                disabled={submitting}
                             />
                         </Form.Item>
 
-                        {/* 表单操作按钮(添加加载状态) */}
+                        {/* 表单操作按钮 */}
                         <Form.Item>
                             <div className="flex justify-end gap-2">
-                                <Button onClick={handleCancel} type="default" disabled={submitting}>
+                                <Button
+                                    onClick={handleCancel}
+                                    type="default"
+                                    disabled={submitting}
+                                >
                                     取消
                                 </Button>
                                 <Button

+ 6 - 2
src/components/products/ProductMenuClient.tsx

@@ -51,7 +51,11 @@ export default function ProductMenu({menuItems, products}: ProductMenuProps) {
         () => products.filter((product) => product.productType === selectedKey),
         [products, selectedKey]
     );
-
+// 在 ProductMenuClient.tsx 中
+    const normalizedProducts = showProductList.map(item => ({
+        ...item,
+        productUrl: item.productUrl ?? undefined // 把 null 转成 undefined
+    }))
     useEffect(() => {
         if (window.innerWidth > 640) {
             for (const item of menuItems) {
@@ -139,7 +143,7 @@ export default function ProductMenu({menuItems, products}: ProductMenuProps) {
                     <div className="sm:hidden mt-3 mb-10">
                         <SubTitle title={findLabelByKey(selectedKey)} customStyle={{textAlign: "center"}}/>
                     </div>
-                    <ProductGrid products={showProductList}/>
+                    <ProductGrid products={normalizedProducts}/>
                 </div>
             </div>
         </>

+ 274 - 0
src/utils/clientRequest.ts

@@ -0,0 +1,274 @@
+// src/types/request.ts
+
+/**
+ * API 通用响应格式
+ */
+export interface ApiResponse<T = any> {
+    data: T
+    code: number
+    msg: string
+    success?: boolean // 兼容部分后端返回格式
+}
+
+/**
+ * 客户端请求配置
+ * 剔除服务端专属的 cache/next 配置
+ */
+export interface ClientRequestConfig {
+    /** 请求超时时间(毫秒) */
+    timeout?: number
+    /** 请求头 */
+    headers?: Record<string, string>
+    /** 取消请求信号 */
+    signal?: AbortSignal
+    /** 是否携带凭证(cookie) */
+    credentials?: RequestCredentials
+}
+
+/**
+ * GET/DELETE 请求专用配置
+ */
+export interface ClientGetDeleteConfig extends ClientRequestConfig {
+    /** 数组参数格式化方式 */
+    arrayFormat?: "indices" | "repeat" | "comma"
+}
+
+/**
+ * 客户端请求错误类型
+ */
+export type ClientRequestError = Error & {
+    /** HTTP 状态码 */
+    statusCode?: number
+    /** 原始错误信息 */
+    originalError?: Error
+    /** 后端返回的错误信息 */
+    responseMsg?: string
+}
+/**
+ * 客户端请求工具类
+ * 专用于 Next.js "use client" 组件
+ */
+class ClientHttpClient {
+    // 基础配置
+    private readonly baseURL: string
+    private readonly defaultTimeout: number = 15000 // 默认超时 15 秒
+    private readonly defaultHeaders: Record<string, string> = {
+        'Content-Type': 'application/json;charset=UTF-8',
+        'X-Client-Type': 'browser', // 标记客户端请求
+    }
+
+    constructor() {
+        // 从环境变量获取基础地址,兜底值保证代码不报错
+        this.baseURL = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8080"
+    }
+
+    /**
+     * 通用请求核心方法
+     */
+    private async request<TResponse = any>(
+        method: 'GET' | 'POST' | 'PUT' | 'DELETE',
+        url: string,
+        options: {
+            params?: Record<string, any> | null // GET/DELETE 参数
+            data?: Record<string, any> | null    // POST/PUT 数据
+            config?: ClientRequestConfig
+        }
+    ): Promise<ApiResponse<TResponse>> {
+        const { params, data, config = {} } = options
+        let fullUrl = `${this.baseURL}${url}`
+
+        // 处理 GET/DELETE 请求参数
+        if ((method === 'GET' || method === 'DELETE') && params) {
+            fullUrl = this.buildUrl(url, params, (config as ClientGetDeleteConfig)?.arrayFormat)
+        }
+
+        // 创建超时控制器
+        const controller = new AbortController()
+        const timeoutId = setTimeout(() => controller.abort(), config.timeout || this.defaultTimeout)
+
+        try {
+            // 构建请求选项
+            const fetchOptions: RequestInit = {
+                method,
+                headers: { ...this.defaultHeaders, ...config.headers },
+                signal: config.signal || controller.signal,
+                credentials: config.credentials || 'same-origin', // 默认携带同域 cookie
+            }
+
+            // 处理 POST/PUT 请求体
+            if ((method === 'POST' || method === 'PUT') && data) {
+                fetchOptions.body = JSON.stringify(data)
+            }
+
+            // 发送请求
+            const response = await fetch(fullUrl, fetchOptions)
+            clearTimeout(timeoutId)
+
+            // 处理 HTTP 错误状态
+            if (!response.ok) {
+                const error = new Error(`请求失败:${response.statusText}`) as ClientRequestError
+                error.statusCode = response.status
+
+                // 尝试解析后端返回的错误信息
+                try {
+                    const errorData = await response.json() as ApiResponse
+                    // (确保类型安全)
+                    error.responseMsg = errorData.msg ?? errorData.data?.msg ?? error.message ?? '请求失败'
+                    error.message = error.responseMsg
+                } catch {
+                    // 非 JSON 响应时使用默认提示
+                    error.message = `HTTP ${response.status}: 请求失败`
+                }
+
+                throw error
+            }
+
+            // 解析响应数据
+            const responseData = await response.json() as ApiResponse<TResponse>
+            // 兼容不同的后端响应格式
+            if (responseData.code !== undefined && responseData.code !== 200 && !responseData.success) {
+                const error = new Error(responseData.msg || '业务处理失败') as ClientRequestError
+                error.statusCode = responseData.code
+                error.responseMsg = responseData.msg
+                throw error
+            }
+
+            return responseData
+        } catch (error) {
+            clearTimeout(timeoutId)
+            // 统一错误处理
+            throw this.handleError(error as Error)
+        }
+    }
+
+    /**
+     * 构建带参数的 URL
+     */
+    private buildUrl(
+        url: string,
+        params: Record<string, any>,
+        arrayFormat: "indices" | "repeat" | "comma" = "repeat"
+    ): string {
+        if (!params || Object.keys(params).length === 0) {
+            return `${this.baseURL}${url}`
+        }
+
+        const searchParams = new URLSearchParams()
+
+        Object.entries(params).forEach(([key, value]) => {
+            if (value === undefined || value === null) return
+
+            if (Array.isArray(value)) {
+                switch (arrayFormat) {
+                    case 'comma':
+                        searchParams.set(key, value.map(String).join(','))
+                        break
+                    case 'repeat':
+                        value.forEach(v => searchParams.append(key, String(v)))
+                        break
+                    case 'indices':
+                    default:
+                        value.forEach((v, i) => searchParams.set(`${key}[${i}]`, String(v)))
+                        break
+                }
+            } else {
+                searchParams.set(key, String(value))
+            }
+        })
+
+        const queryString = searchParams.toString()
+        return queryString ? `${this.baseURL}${url}?${queryString}` : `${this.baseURL}${url}`
+    }
+
+    /**
+     * 统一错误处理(客户端友好提示)
+     */
+    private handleError(error: Error): ClientRequestError {
+        const clientError = error as ClientRequestError
+        clientError.originalError = { ...error }
+
+        // 超时错误
+        if (error.name === 'AbortError') {
+            clientError.message = '请求超时,请检查网络或稍后重试'
+            clientError.statusCode = 408
+        }
+        // 网络错误
+        else if (error instanceof TypeError && error.message.includes('fetch')) {
+            clientError.message = '网络连接失败,请检查您的网络设置'
+            clientError.statusCode = 0
+        }
+        // 未知错误
+        else if (!clientError.statusCode) {
+            clientError.message = clientError.message || '请求失败,请联系客服'
+            clientError.statusCode = 500
+        }
+
+        return clientError
+    }
+
+    /**
+     * 客户端 GET 请求
+     */
+    async get<TResponse = any>(
+        url: string,
+        params?: Record<string, any> | null,
+        config?: ClientGetDeleteConfig
+    ): Promise<ApiResponse<TResponse>> {
+        return this.request<TResponse>('GET', url, { params, config })
+    }
+
+    /**
+     * 客户端 POST 请求
+     */
+    async post<TResponse = any>(
+        url: string,
+        data?: Record<string, any> | null,
+        config?: ClientRequestConfig
+    ): Promise<ApiResponse<TResponse>> {
+        return this.request<TResponse>('POST', url, { data, config })
+    }
+
+    /**
+     * 客户端 PUT 请求
+     */
+    async put<TResponse = any>(
+        url: string,
+        data?: Record<string, any> | null,
+        config?: ClientRequestConfig
+    ): Promise<ApiResponse<TResponse>> {
+        return this.request<TResponse>('PUT', url, { data, config })
+    }
+
+    /**
+     * 客户端 DELETE 请求
+     */
+    async delete<TResponse = any>(
+        url: string,
+        params?: Record<string, any> | null,
+        config?: ClientGetDeleteConfig
+    ): Promise<ApiResponse<TResponse>> {
+        return this.request<TResponse>('DELETE', url, { params, config })
+    }
+
+    /**
+     * 获取当前配置信息(调试用)
+     */
+    getConfig() {
+        return {
+            baseURL: this.baseURL,
+            defaultTimeout: this.defaultTimeout,
+            defaultHeaders: { ...this.defaultHeaders }
+        }
+    }
+}
+
+// 创建单例实例(避免重复创建)
+const clientHttpClient = new ClientHttpClient()
+
+// 导出简化的方法名(方便使用)
+export const clientGet = clientHttpClient.get.bind(clientHttpClient)
+export const clientPost = clientHttpClient.post.bind(clientHttpClient)
+export const clientPut = clientHttpClient.put.bind(clientHttpClient)
+export const clientDelete = clientHttpClient.delete.bind(clientHttpClient)
+
+export { clientHttpClient }

+ 1 - 92
src/utils/request.ts

@@ -18,14 +18,8 @@ interface RequestConfig {
 }
 
 // 定义可能的错误类型
-// type HttpClientError = Error
+type HttpClientError = Error
 
-// 定义可能的错误类型
-type HttpClientError = Error & {
-  statusCode?: number // 新增:记录 HTTP 状态码
-  originalError?: Error // 新增:保留原始错误
-  isClientError?: boolean // 新增:标记是否为客户端错误
-}
 class ServerHttpClient {
   private baseURL: string
   private defaultTimeout: number
@@ -39,90 +33,6 @@ class ServerHttpClient {
     }
   }
 
-  /**
-   * 通用请求方法(核心复用逻辑)
-   */
-  private async request<TResponse = any>(
-      method: "GET" | "POST" | "PUT" | "DELETE",
-      url: string,
-      options: {
-        params?: Record<string, any> | null
-        data?: Record<string, any> | null
-        config?: RequestConfig & { arrayFormat?: "indices" | "repeat" | "comma" }
-        isClientRequest?: boolean // 标记是否为客户端请求
-      }
-  ): Promise<ApiResponse<TResponse>> {
-    const { params, data, config, isClientRequest = false } = options
-    let fullUrl = `${this.baseURL}${url}`
-
-    // 处理 GET/DELETE 的 URL 参数
-    if ((method === "GET" || method === "DELETE") && params) {
-      fullUrl = this.buildUrl(url, params, config?.arrayFormat)
-    }
-
-    // 创建请求控制器(处理超时)
-    const controller = new AbortController()
-    const timeoutId = setTimeout(() => controller.abort(), config?.timeout ?? this.defaultTimeout)
-
-    try {
-      // 客户端请求移除服务端缓存配置
-      const fetchOptions: RequestInit = {
-        method,
-        headers: { ...this.defaultHeaders, ...config?.headers },
-        body: data ? JSON.stringify(data) : undefined,
-        signal: config?.signal ?? controller.signal,
-      }
-
-      // 仅服务端请求保留缓存配置
-      if (!isClientRequest) {
-        fetchOptions.cache = config?.cache as RequestCache
-        ;(fetchOptions as any).next = config?.next
-      }
-
-      const response = await fetch(fullUrl, fetchOptions)
-
-      clearTimeout(timeoutId)
-
-      // 处理 HTTP 错误状态
-      if (!response.ok) {
-        let errorMsg = `HTTP ${response.status}: ${response.statusText}`
-        try {
-          const errorData = await response.json()
-          errorMsg = (errorData as ApiResponse)?.msg || errorMsg
-        } catch {
-          // 解析错误数据失败时使用默认提示
-        }
-
-        const error = new Error(errorMsg) as HttpClientError
-        error.statusCode = response.status
-        error.isClientError = isClientRequest
-        throw error
-      }
-
-      // 解析响应数据
-      const responseData: ApiResponse<TResponse> = await response.json()
-      return responseData
-    } catch (error) {
-      clearTimeout(timeoutId) // 确保超时器被清理
-      throw this.handleError(error as Error)
-    }
-  }
-
-  /**
-   * 客户端 POST 请求方法(新增)
-   */
-  async clientPost<TResponse = any, TData extends Record<string, any> | null = null>(
-      url: string,
-      data?: TData,
-      config?: Omit<RequestConfig, "cache" | "next">, // 客户端禁用服务端缓存配置
-  ): Promise<ApiResponse<TResponse>> {
-    return this.request<TResponse>("POST", url, {
-      data,
-      config,
-      isClientRequest: true // 标记为客户端请求
-    })
-  }
-
   async serverGet<TResponse = any, TParams extends Record<string, any> | null = Record<string, any> | null>(
       url: string,
       params?: TParams,
@@ -329,7 +239,6 @@ export const serverGet = serverHttpClient.serverGet.bind(serverHttpClient)
 export const serverPost = serverHttpClient.serverPost.bind(serverHttpClient)
 export const serverPut = serverHttpClient.serverPut.bind(serverHttpClient)
 export const serverDelete = serverHttpClient.serverDelete.bind(serverHttpClient)
-export const clientPost = serverHttpClient.clientPost.bind(serverHttpClient) // 导出新增的clientPost
 
 export type { ApiResponse, RequestConfig, HttpClientError }
 export { serverHttpClient }