# 豆包 Widget Template Library 参考

`@doubao-dev/template` 是面向 widget 开发的卡片模板库。模板已经把标题栏、内容区、信息区、价格和操作按钮等结构组合好，开发者只需要选模板并传业务数据。

## 核心规则

1. **一个 widget 一张卡片**：入口组件直接 return 模板组件，不要再包 `view` / `scroll-view`。
2. **统一从 `@doubao-dev/template` 导入**模板组件和类型；新 Widget 默认直接导出组件函数。
3. **优先用模板，不手搓卡片**：先按选型表找模板并使用其公开 Props；匹配不上时按模板能力或新设计需求确认。
4. **新代码不传 `children`**：部分模板和列表项仍保留该属性，但已经废弃，只用于兼容旧项目。生成或修改代码时，改用当前模板的结构化字段或已声明的具名 `ReactNode` 字段；没有匹配字段时按模板能力或新设计需求确认。
5. **显式标注列表类型**：每个模板导出 `XxxProps` 和列表项类型，例如 `ContentCardItem`、`CheckoutCardProductItem`。
6. **header/footer 统一约定**：带标题栏和底部操作区的模板统一使用 `header` / `footer`。`header` 只控制标题栏，`footer` 只控制底部按钮，二者不影响内容区展示数量。
7. **底部按钮对象**：`footer.primaryActionButton` / `footer.secondaryActionButton` 内传 `text`、`onClick`、`disabled`、`loading`、`throttle`。只传一个按钮时占据整行。

`Title`、`Tag` 和 `Price` 是卡片内容中的标题、标签、价格展示组件，不参与根卡片选型；需要一至两行标题、业务标签或自定义价格排版时从 `@doubao-dev/template/primitives` 导入使用。

## 选型决策顺序

按业务意图依次判断：

1. 核对并提交订单：通用商品、机票或出行票务订单都使用 `CheckoutCard`。
2. 展示多条交通行程：用 `TransitCard`。
3. 在多个报价、权益或服务方案中选择：用 `PriceActionCard`。
4. 让用户在候选项中确认或执行固定跳转：用 `AskHumanCard`；单个或两个全宽跳转项使用 `variant="jump"`。
5. 其余图文、商品或服务列表：用 `ContentCard`。

不能按以上意图选型时，不要直接拼新的卡片外壳。先查现有模板的公开 Props；无法表达时将其作为模板能力或新设计需求确认。

## Widget 骨架

```tsx
import { ContentCard, type ContentCardItem } from '@doubao-dev/template';

const items: ContentCardItem[] = [
  {
    key: 'a',
    title: '主标题',
    subtitle: '4199 元 | 商品描述',
    description: '补充描述',
    thumbnailSrc: 'https://example.com/p.png'
  }
];

export default function ProductCard() {
  return (
    <ContentCard
      items={items}
      footer={{
        secondaryActionButton: {
          text: '查看更多',
          onClick: () => console.log('view more')
        },
        primaryActionButton: {
          text: '立即下单',
          onClick: () => console.log('submit')
        }
      }}
    />
  );
}
```

## 选型指南

按“卡片要表达什么”选模板：

| 场景 | 模板 | 一句话 |
|------|------|--------|
| 商品/服务/内容列表 | `ContentCard` | 通用图文列表项，每项可带副标题、缩略内容和右侧操作 |
| 多条可操作报价/权益/结果 | `PriceActionCard` | 每行左价格 + 中信息 + 右按钮，可配底部操作区 |
| 下单/支付确认（通用电商） | `CheckoutCard` | 商品摘要 + 提单信息 + 费用汇总 + 合计 + 操作按钮 |
| 机票/出行下单确认 | `CheckoutCard` | 商品或票务摘要 + 订单信息 + 费用明细 + 合计 + 操作按钮 |
| 交通票务推荐列表 | `TransitCard` | 多条出发/到达行程，全量展示 items |
| 让用户在多个候选里人工确认 | `AskHumanCard` | 一组可点击选项，跳过入口也通过 items 表达 |
| 单个全宽操作按钮（任务态） | `AskHumanCard`（`variant="jump"`） | 一个可带右箭头的全宽按钮 |

## 通用约定

- **`header`**：标题栏配置，常用 `actionText` / `showAction` / `onActionClick`。
- **`footer`**：底部操作区配置，常用 `primaryActionButton` / `secondaryActionButton`。
- **`children`**：部分模板为兼容旧项目保留的废弃属性，新代码不要使用。
- **图片资源**：传运行时可访问的图片 URL；本地图片放到业务项目 `src/assets` 后静态 `import` 再传入，不要直接写 `'/assets/xxx.png'`。`ContentCard` 普通缩略图使用 `thumbnailSrc`，需要媒体节点时使用 `thumbnail`，且节点优先。
- **`className` / `style` / `onClick`**：作用在卡片根节点。
- **列表项 `key`**：不传默认用数组下标，建议显式传稳定 key。
- **行数限制**：`PriceActionCard.infoRows` 最多展示前 2 行；`ContentCard` 和 `TransitCard` 不再根据 footer 折叠内容。

## 详细 Props 参考

每个模板完整 props、列表项字段、行为约束见 [props.md](props.md)。先查选型表锁定模板，再到该文件确认字段。

## 定制边界

回答“能否这样配置”时，先确认当前 props 是否支持。没有对应 props 或明确内容槽位的结构性需求，不要通过额外包裹节点或样式覆盖伪造，按模板能力或新设计需求处理。底部操作区最多包含一个主按钮和一个次按钮。

详见 [design-boundary.md](design-boundary.md)。

## 常见错误

| 错误 | 修正 |
|------|------|
| `render` 里把模板包在 `view` / `scroll-view` 里 | 直接 `return <XxxCard ... />` |
| 想做卡片却手写 `view`+`text`+`image` 拼布局 | 先查选型表和公开 Props；没有内容槽位时按新模板或设计需求确认 |
| 继续传 `moreText` / `primaryActionText` / `secondaryActionText` | 改为 `header.actionText` 和 `footer.primaryActionButton` / `footer.secondaryActionButton` |
| 需要单个全宽跳转操作 | 使用 `AskHumanCard variant="jump"` 和一个 `items` 项 |
| 列表项不传 `key` 导致更新错乱 | 给每个 item 传稳定 key |
| 用 `onClick` 当整卡和按钮的混用回调 | 底部按钮用 `footer.*ActionButton.onClick`，列表项右侧按钮用对应 action 的回调；`onClick` 是整卡或列表项点击 |
| 期望 `TransitCard` 自动折叠 4 条以上内容 | 模板全量展示 `items`，需要折叠时由业务先处理数据并通过 footer 提供入口 |
| 从 `react` import 模板/类型 | 模板组件与类型从 `@doubao-dev/template` 导入 |
