npm package: @savehmi/request
Description: 公共请求库：基于 axios 的封装，支持 mock、重复请求去重、统一 response 处理等能力
Author: shmi_devops
Latest version: 1.0.0

# @savehmi/request

基于 **axios** 的公共请求库，来源于 `@chaoswise/request`，提供：

- **统一请求封装**：`fetchGet` / `fetchPost` / `fetchPut` / `fetchDelete` / `upload` / `downloadFile`
- **全局 axios 实例**：`axiosInstance`，支持统一拦截器
- **请求去重 & 取消**：`pending` / `clearPending`
- **Mock 支持**：`mockInstance` + `initRequest.mock` 配置
- **统一 response 处理**：`initRequest.config.handleResponse` + `formateResponse`

## 安装

```bash
npm install @savehmi/request
# 或
yarn add @savehmi/request
```

依赖版本（与原库一致）：

- `axios@^0.21.1`
- `axios-mock-adapter@1.17.0`
- `qs@^6.5.2`

---

## API 一览

```js
import {
  initRequest,
  mockInstance,
  axiosInstance,
  clearPending,
  pending,
  fetchGet,
  fetchPut,
  fetchDelete,
  fetchPost,
  upload,
  downloadFile,
  formateResponse,
} from "@savehmi/request";
```

### initRequest

初始化配置并创建请求实例 `axiosInstance`：

```js
initRequest({
  config: {
    // 请求错误码回调
    statusCallback: {
      "401": () => {},
      "403": () => {},
    },
    // 是否启用 mock 数据
    useMock: true,
    // mock 延迟 ms
    delayResponse: 500,
    // 统一处理请求结果（成功 / 失败）
    handleResponse: (res, error, rawResponse) => {
      if (error) {
        // 错误处理
      } else {
        // 响应处理
      }
    },
    // 是否开启登陆验证（401 统一跳转）
    checkLogin: false,
    // sso 登出校验地址（可选）
    restapi: "",
  },
  // axios 默认配置
  defaults: {
    baseURL: "",
  },
  // mock 配置（依赖 axios-mock-adapter）
  mock: [
    {
      method: "onGet",
      url: "/mock/url",
      res: {
        code: 100000,
        data: {},
      },
    },
  ],
});
```

### mockInstance

直接使用 `axios-mock-adapter` 的实例做更灵活的 Mock：

```js
import { mockInstance } from "@savehmi/request";

mockInstance.onGet("/get/list").reply(200, {
  data: [],
});
```

### axiosInstance

对外暴露的 axios 实例，可以自定义拦截器：

```js
import { axiosInstance } from "@savehmi/request";

axiosInstance.interceptors.request.use(
  (config) => {
    // 统一附加 header
    config.headers.language = "zh-CN";
    return config;
  },
  (error) => Promise.reject(error),
);
``>

### clearPending / pending

管理请求取消：

```js
import { clearPending, fetchPost, pending } from "@savehmi/request";

// 取消全部正在进行的请求（例如页面卸载时）
clearPending();

// 发起请求并保留 cancel
const req = fetchPost("/api/demo");
req.cancel && req.cancel();

// 通过 pending Map 定位并取消某个请求
const cancel = pending.get("/api/demo");
cancel && cancel("/api/demo");
pending.delete("/api/demo");
```

### fetchGet / fetchPost / fetchPut / fetchDelete

包装过的 HTTP 请求方法：

```js
import { fetchGet, fetchPost } from "@savehmi/request";

fetchGet("/get/list", {
  params: { name: "张三" },
}).then((res) => {
  // 成功
});

fetchPost("/post/save", {
  data: { name: "张三" },
}).catch((err) => {
  // 失败
});
```

### upload

文件上传：

```js
import { upload } from "@savehmi/request";

upload(
  "/upload",
  { file, otherField: "xxx" },
  (progressEvent) => {
    // 上传进度回调
  },
  {}, // 额外 axios 配置
);
```

### downloadFile

文件下载（默认 .xlsx）：

```js
import { downloadFile } from "@savehmi/request";

downloadFile(contentBlob, "export-file", {
  type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
});
```

### formateResponse

后端返回结构归一化工具：

```js
import { formateResponse } from "@savehmi/request";

const res = formateResponse({
  error_code: "000000",
  error_msg: "success",
  data: {},
});

// res.data 会被格式化为：
// {
//   code: 100000,
//   msg: 'success',
//   status: 'success',
//   data: {}
// }
```