> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 操作

> Aftersell Cart SDK 中用于读取和更改购物车的所有操作：打开、关闭、添加、移除、更新数量、更换变体、读取状态和格式化金额。

操作用于**读取和更改购物车**。它们位于 `window.aftersell.cart.actions` 下。

<Note>
  操作要在**购物车就绪后**运行，即在 `ready()` 或[事件](/zh/aftersell/cart/sdk-events)处理函数内部。
</Note>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log(state.itemCount, 'items');
});
```

<Note>
  \*\*在购物车加载之前，操作是桩函数。\*\*每个操作会在控制台输出一条指明操作名称的警告，例如 `cart.actions.addItem() called before the cart loaded`，然后不做任何事。异步操作仍会 resolve 一个 Promise，所以 `.then()` 链会正常运行而不是抛出错误；`getCart()` 返回 `null`，`formatMoney()` 返回空字符串。

  过早调用不会破坏任何东西，但也不会发生任何事。当某个操作看似没有效果时，请留意控制台中的这条警告。
</Note>

<div id="every-action">
  ## 所有操作
</div>

| 操作                                                       | 签名                                   | 返回值                     | 作用                 |
| -------------------------------------------------------- | ------------------------------------ | ----------------------- | ------------------ |
| [`open`](#open-and-close)                                | `open()`                             | 无                       | 打开抽屉。              |
| [`close`](#open-and-close)                               | `close()`                            | 无                       | 关闭抽屉。              |
| [`getCart`](#getcart)                                    | `getCart()`                          | `AftersellCart \| null` | 读取当前购物车。           |
| [`formatMoney`](#formatmoneycents)                       | `formatMoney(cents)`                 | `string`                | 格式化金额用于显示。         |
| [`addItem`](#additemvariantid-quantity)                  | `addItem(variantId, quantity?)`      | `Promise`               | 添加一个变体。            |
| [`removeItem`](#removeitemkey)                           | `removeItem(key)`                    | `Promise`               | 移除一行。              |
| [`updateItemQuantity`](#updateitemquantitykey-quantity)  | `updateItemQuantity(key, quantity)`  | `Promise`               | 设置某行的数量。           |
| [`replaceLineVariant`](#replacelinevariantkey-variantid) | `replaceLineVariant(key, variantId)` | `Promise`               | 更换某行的变体。           |
| [`refresh`](#refresh)                                    | `refresh()`                          | `Promise`               | 从 Shopify 重新获取购物车。 |
| [`visualRefresh`](#visualrefresh)                        | `visualRefresh()`                    | 无                       | 重绘但不重新获取。          |

<Warning>
  从 `cart_updated` 处理函数中调用操作可能导致循环。请先阅读[两条规则](/zh/aftersell/cart/sdk-events#the-two-rules)。
</Warning>

***

<div id="drawer">
  ## 抽屉
</div>

<div id="open-and-close">
  ### open 和 close
</div>

打开或关闭购物车抽屉。两者都是同步的，不接受参数。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Open the drawer from your own cart link.
document.querySelector('#my-cart-link').addEventListener('click', (event) => {
  event.preventDefault();
  window.aftersell.cart.actions.open();
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Close it after the shopper does something in a custom block.
window.aftersell.cart.actions.close();
```

***

<div id="reading">
  ## 读取
</div>

<div id="getcart">
  ### getCart()
</div>

返回当前的[购物车对象](/zh/aftersell/cart/sdk-cart-object)，在加载完成之前返回 `null`。结果是一个**副本**，所以修改它不会更改真实的购物车。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  if (!state) return; // the initial load failed

  console.log(state.itemCount, 'items,', state.items.length, 'lines');
  console.log('Total:', window.aftersell.cart.actions.formatMoney(state.totalPrice));
});
```

由于它是快照，不要长期持有结果；每次需要最新数据时重新读取。在事件处理函数中，你已经通过载荷拿到了最新的购物车，所以在那里调用 `getCart()` 是多余的。

<div id="formatmoneycents">
  ### formatMoney(cents)
</div>

使用商店的货币格式对以最小货币单位表示的金额进行格式化。SDK 中的所有价格都以分为单位，这就是把它们转成可显示内容的方式。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.actions.formatMoney(5779);  // "$57.79"
window.aftersell.cart.actions.formatMoney(0);     // "$0.00"
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Show the cart total in your own header element.
window.aftersell.cart.events.on('cart_updated', (state) => {
  document.querySelector('#header-total').textContent =
    window.aftersell.cart.actions.formatMoney(state.totalPrice);
});
```

用 [`configure({ money_format })`](/zh/aftersell/cart/sdk-configure#money_format) 覆盖格式。

***

<div id="changing-the-cart">
  ## 更改购物车
</div>

<Note>
  商品操作通过 Shopify 的 **`key`** 而不是变体 ID 来标识行，因为购物车可以在多个带有不同属性的行中持有同一个变体。从 `getCart().items[n].key` 读取它。
</Note>

<div id="additemvariantid-quantity">
  ### addItem(variantId, quantity?)
</div>

向购物车添加一个变体。`quantity` 默认为 `1`。在购物车稳定后 resolve。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Add one, then show the shopper.
window.aftersell.cart.actions.addItem(41720671830082).then(() => {
  window.aftersell.cart.actions.open();
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Add a specific quantity.
window.aftersell.cart.actions.addItem(41720671830082, 3);
```

添加已在购物车中的变体会增加该行的数量而不是创建第二行，前提是现有行没有行项目属性。带有属性的行是独立的行，所以你会得到新的一行。

<div id="removeitemkey">
  ### removeItem(key)
</div>

完全移除一行。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Remove any free line from the cart.
const gift = window.aftersell.cart.actions
  .getCart()
  .items.find((line) => line.finalLinePrice === 0);
if (gift) window.aftersell.cart.actions.removeItem(gift.key);
```

<div id="updateitemquantitykey-quantity">
  ### updateItemQuantity(key, quantity)
</div>

设置某行的数量。传入 `0` 会移除该行。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const line = window.aftersell.cart.actions.getCart().items[0];
if (line) window.aftersell.cart.actions.updateItemQuantity(line.key, 3);
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Cap a line at one unit.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    if (line.variantId === LIMITED_VARIANT_ID && line.quantity > 1) {
      window.aftersell.cart.actions.updateItemQuantity(line.key, 1);
    }
  });
});
```

第二个示例可以安全地从 `cart_updated` 中运行，因为 `> 1` 检查在第二次执行时为 false。参阅[两条规则](/zh/aftersell/cart/sdk-events#the-two-rules)。

<div id="replacelinevariantkey-variantid">
  ### replaceLineVariant(key, variantId)
</div>

更换某行的变体，同时保留其数量和属性。适用于购物车内的尺码或口味切换器。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const line = window.aftersell.cart.actions.getCart().items[0];
window.aftersell.cart.actions.replaceLineVariant(line.key, 41720671862850);
```

<Warning>
  更换时该行的**销售计划会重置**。订阅行会变成一次性购买，除非你重新应用计划。
</Warning>

更换是一次添加后跟一次移除，而不是原地编辑，所以结果是**新的一行**：它获得新的 `key` 并落在购物车末尾。之后请重新读取 `getCart()`，而不要重用你传入的 key。

***

<div id="refreshing">
  ## 刷新
</div>

<div id="refresh">
  ### refresh()
</div>

从 Shopify 重新获取购物车。当 SDK 之外的东西更改了购物车而抽屉没有察觉时使用它。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// After a raw Ajax API call of your own.
fetch('/cart/add.js', { method: 'POST', /* … */ })
  .then(() => window.aftersell.cart.actions.refresh())
  .then(() => { window.aftersell.cart.actions.open(); });
```

大多数时候你不需要它，因为 Aftersell 已经监听 Shopify 的标准购物车事件并自行重新获取。当自定义集成绕过了这些事件时才需要它。

<div id="visualrefresh">
  ### visualRefresh()
</div>

重新运行渲染转换，但不从 Shopify 重新获取购物车。你很少需要它：注册（或注销）[行转换](/zh/aftersell/cart/sdk-hooks#registerlinetransform)、[比较器](/zh/aftersell/cart/sdk-hooks#registerlinecomparator)、[增强器](/zh/aftersell/cart/sdk-hooks#registercartenricher)或任一[订阅 hook](/zh/aftersell/cart/sdk-hooks#registersubscriptionoptionstransform) 都会自动触发一次。只有两个加入购物车 hook 不会，因为它们不改变屏幕上已有的任何内容。

当转换*所依赖的*内容发生变化而购物车本身没变时使用它：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// A currency switcher changed the format: repaint prices already on screen.
window.myTheme.onCurrencyChange((currency) => {
  window.aftersell.cart.configure({ money_format: FORMATS[currency] });
  window.aftersell.cart.actions.visualRefresh();
});
```

***

<div id="notes-and-edge-cases">
  ## 注意事项和边界情况
</div>

* \*\*异步操作在变更稳定后 resolve。\*\*等待一个操作可以让你在购物车确实更新后再安排后续工作。
* \*\*`getCart()` 返回副本。\*\*修改它对真实购物车没有任何影响。
* \*\*没有折扣码操作。\*\*已应用的折扣码可以在购物车上读取（`discountCodes`、`totalDiscount`）以及按行读取（`discountAllocations`）；购物者通过[折扣码](/zh/aftersell/cart/discount-code-block)区块应用它们。
* \*\*没有购物车属性或备注操作。\*\*属性可以在购物车对象上读取；购物者通过[备注](/zh/aftersell/cart/notes-block)区块编写备注。
* **要隐藏一行而不是移除它**，请使用 [`registerLineTransform`](/zh/aftersell/cart/sdk-hooks#registerlinetransform)。移除会改变购物者的总额；隐藏不会。

<div id="where-to-go-next">
  ## 后续阅读
</div>

* **[购物车对象](/zh/aftersell/cart/sdk-cart-object)**：`getCart()` 返回的内容。
* **[事件](/zh/aftersell/cart/sdk-events)**：何时运行这些操作。
* **[Hooks](/zh/aftersell/cart/sdk-hooks)**：改变行的渲染方式而不是更改购物车。
* **[使用案例](/zh/aftersell/cart/sdk-use-cases)**：常见需求的完整解决方案。
