# 简介

### SDK 组成

1. `SDK` 由 `api` 和 `ui` 两类功能组成
2. `api` 主要由：基础网络 `io`、商城标准接口、基础工具库 组成
3. `ui` 提供主题各 ui 模块的调用接口

### 入口对象

由主题在全局挂载 SDK 的 `入口对象`，直接使用 `mshop` 或 `window.mshop` 即可调用 SDK 功能

```typescript
declare global {
    /** 商城SDK调用入口对象 */
    export namespace mshop {
        /** 当前库的版本号 */
        const version: string;
        /** 店铺信息 */
        const shopInfo: ShopInfo;
        /** IO功能 */
        const io: {
            /** http请求 */
            http: IHttp,
            /** http请求拦截器 */
            interceptor: IHttpInterceptor,
        };
        /** 全局事件处理器实例 */
        const events: GlobalEventEmitter;
        /** 先派发后注册的再次派发策略类型 */
        const eventEmitterEnsureTypes: typeof EventEmitterEnsureTypes;
        /** 事件处理器的实现类 */
        const EventEmitterType: typeof EventEmitter;
        /** 所有商城标准接口 */
        const api: APIs;
        /** 工具函数封装 */
        const tool: Tools;
        /** 拦截器实现类 */
        const interceptors: Interceptors;
        /** 标准结果定义 */
        const results: IResults;
        /** HTTP 错误类型 */
        const httpErrorFlags: typeof HttpErrorFlags;

        // ...其他模块

        /** ui模块 */
        namespace ui {
            /** 购物车模块ui接口 */
            const cart: ICart;
            /** 商品模块ui接口 */
            const product: IProduct;
            // ...其他UI模块

        }
    }
}
```

### 使用方法

为了开发时能有类型感知和智能提示，SDK 提供了对应的类型定义库：

```
npm i --save-dev @mshop/api-types @mshop/ui-cart-types
```

或

```
yarn add --dev @mshop/api-types @mshop/ui-cart-types
```

使用 `mshop` 即可拥有智能提示：

```typescript
console.log(mshop.shopInfo)
/*
{
  // 是否在装修编辑器模式
  "designMode": false,
  // 是否在主题预览模式
  "previewMode": false,
}
*/
```

### 购物车模块

#### 1. 购物车事件调用示例

```typescript
// 注册全局的“单商品加购成功”事件
mshop.ui.cart.on(mshop.ui.cart.cartEventNames.ADD_TO_CART, (param: Readonly<AddToCartParamMergeCart>, resultCart: Readonly<RawCartType>) => {
  // TODO
});
// 注册全局的“批量加购成功”事件
mshop.ui.cart.on(mshop.ui.cart.cartEventNames.BATCH_ADD_TO_CART, (param: Readonly<BatchAddToCartParamMergeCart>, resultCart: Readonly<RawCartType>) => {
  // TODO
});
```

#### 2. 加购和批量加购调用示例

```typescript
// 调用主题的单商品加购api
mshop.ui.cart.addToCart({
  // 本次加购是否清理购物车的原有商品,默认为false
  clear: false,
  // 商品id
  product_id: 111,
  // 变种id
  variant_id: 110,
  // 加购数量
  quantity: 2,
  // 加购来源标识，用于统计识别
  addType: 'customcode_email',
  // 商品所属分类名列表，需要尽量提供，统计识别需要
  categoryNames: [],
  // 其他自定义属性
  properties: {},
  // 加购成功后是否展示购物车，默认为true，可以不传
  showCart: true,
  onAddToCartActivated: () => {
    // 这里表示加购动作完成，可以将按钮loading之类的状态取消
  },
}).then(ret => {
  if (ret.success === false) {
    // 加购操作失败了（被已知行为拒绝或中止）
    if (ret.failMessage) {
        // 如果有需要UI提示的消息，由主题完成，不需要调用再次提示（如果调用场景比较特殊，全局吐司提示不够友好，也可以自己补充）
    }
    return;
  }
  // 成功后可以拿到最新的购物车数据
  let cartData = ret.data;
}).catch((err: Error) => {
    // 异常逻辑处理
    console.error(err);
    // 建议继续外抛，让sentry收集到
    throw err;
});
```

```typescript
// 调用主题的批量加购api
mshop.ui.cart.batchAddToCart({
  // 本次加购是否清理购物车的原有商品,默认为false
  clear: false,
  // 加购来源标识，用于统计识别
  addType: 'customcode_email',
  product: [
    {
      // 商品id
      product_id: 111,
      // 变种id
      variant_id: 110,
      // 加购数量
      quantity: 2,
      // 商品所属分类名列表，需要尽量提供，统计识别需要
      categoryNames: [],
    }
  ],
  // 加购成功后是否展示购物车，默认为true，可以不传
  showCart: true,
  onAddToCartActivated: () => {
    // 这里表示加购动作完成，可以将按钮loading之类的状态取消
  },
}).then(ret => {
  if (ret.success === false) {
    // 加购操作失败了（被已知行为拒绝或中止）
    if (ret.failMessage) {
        // 如果有需要UI提示的消息，由主题完成，不需要调用再次提示（如果调用场景比较特殊，全局吐司提示不够友好，也可以自己补充）
    }
    return;
  }
  // 成功后可以拿到最新的购物车数据
  let cartData = ret.data;
}).catch((err: Error) => {
    // 异常逻辑处理
    console.error(err);
    // 建议继续外抛，让sentry收集到
    throw err;
});
```

#### 3. 加购拦截器调用示例

```typescript
// 拦截加购行为
mshop.ui.cart.interceptAddToCart(async (ev) => {
  // 可直接修改ev.params里的字段
  ev.params.addType = 'test';
  // 注意：如果想加上自己的自定义属性，需要使用拓展的方式，避免把其他的功能加的自定义属性覆盖
  ev.params.properties = Object.assign(e.params.properties ?? {}, { a: 1 });
  return;// 修改后直接返回

  // 也可以替换整个参数（一般不需要用到）
  return {
      newParams: ev.params
  };

  // 如果要中断操作：
  return {
      isAbort: true,
      abortMessage: '中止操作并传递中止原因，模块会根据场景进行显示或忽略，不传则不做任何提示',
  };
  
  // 如果要中断操作，但不需要主题的提示UI（比如做加购前表单填写，自己做了表单的聚焦和提示等场景）：
  return {
      isAbort: true,
  };
})
// 拦截批量加购行为
mshop.ui.cart.interceptBatchAddToCart(async (ev) => {
  // 可直接修改ev.params里的字段
  ev.params.addType = 'test';
  // 注意：如果想加上自己的自定义属性，需要使用拓展的方式，避免把其他的功能加的自定义属性覆盖
  ev.params.properties = Object.assign(e.params.properties ?? {}, { a: 1 });
  return;// 修改后直接返回
  
  // 也可以替换整个参数（一般不需要用到）
  return {
      newParams: ev.params
  };

  // 如果要中断操作：
  return {
      isAbort: true,
      abortMessage: '中止操作并传递中止原因，模块会根据场景进行显示或忽略，不传则不做任何提示',
  };
  
  // 如果要中断操作，但不需要主题的提示UI（比如做加购前表单填写，自己做了表单的聚焦和提示等场景）：
  return {
      isAbort: true,
  };
})
```

### 商品模块

#### 1. 详情页获取商品信息用例以及监听商品sku变化

```typescript
// 获取商品详情页信息（其他页面将返回undefined）
const product =  mshop.ui.product.getProductPageInfo()

//注册商品详情页sku变化
mshop.ui.product.getProductPageInfo().onVariantChange((currentVariantAttrs, currentVariant) => {
  /**
   *  currentVariant.buyAble 表示当前变种是否可购买 （false为规格未匹配到变种，或匹配到变种但无库存）
   */ 
  console.log(currentVariantAttrs, currentVariant)
})
```

### 商城标准接口

*（店铺页面嵌入的js，不建议直接调用标准接口）*

#### 购物车api调用示例

```typescript
await mshop.api.cart.addToCart({
    clear: true,
    product_id: productId,
    variant_id: variantId,
    quantity: quantity,
}).then(ret => {
    if (!ret.success) {
        // 加购api失败了（被已知行为拒绝或中止）
        if (ret.failMessage) {
            // 有提供友好显示消息，可以吐司提示   
        }
        return;
    }
    // 成功后可以拿到最新的购物车数据
    let cartData = ret.data;
}).catch((err: HttpError<any>) => {
    // 异常的话可以拿到包含请求详细信息的HttpError异常对象，建议继续抛出
    throw err;
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.hotishop.com/mshop-sdk/intro.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
