# 简介

### 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;
});
```
