@doubao-dev/template 0.0.38 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +116 -44
  2. package/dist/ask-human-card/index.d.ts +1 -3
  3. package/dist/ask-human-card/index.js +4 -8
  4. package/dist/checkout-card/index.d.ts +3 -5
  5. package/dist/checkout-card/index.js +2 -2
  6. package/dist/components/info-section/index.d.ts +0 -2
  7. package/dist/components/info-section/index.js +4 -7
  8. package/dist/components/info-section/styles.css +28 -18
  9. package/dist/components/product-summary/index.d.ts +5 -1
  10. package/dist/components/product-summary/index.js +9 -7
  11. package/dist/components/product-summary/styles.css +25 -16
  12. package/dist/components/route-section/index.d.ts +5 -1
  13. package/dist/components/route-section/index.js +2 -0
  14. package/dist/components/route-section/styles.css +24 -0
  15. package/dist/content-card/index.d.ts +13 -3
  16. package/dist/content-card/index.js +12 -6
  17. package/dist/content-card/styles.css +9 -5
  18. package/dist/index.d.ts +1 -1
  19. package/dist/price-action-card/index.d.ts +8 -2
  20. package/dist/price-action-card/index.js +2 -8
  21. package/dist/price-action-card/styles.css +15 -29
  22. package/dist/primitives/index.d.ts +4 -0
  23. package/dist/primitives/index.js +3 -0
  24. package/dist/primitives/price/index.d.ts +5 -0
  25. package/dist/primitives/price/index.js +56 -0
  26. package/dist/primitives/price/styles.css +130 -0
  27. package/dist/primitives/price/types.d.ts +26 -0
  28. package/dist/primitives/price/types.js +0 -0
  29. package/dist/primitives/tag/index.d.ts +5 -0
  30. package/dist/primitives/tag/index.js +10 -0
  31. package/dist/primitives/tag/styles.css +70 -0
  32. package/dist/primitives/tag/types.d.ts +14 -0
  33. package/dist/primitives/tag/types.js +0 -0
  34. package/dist/primitives/title/index.d.ts +5 -0
  35. package/dist/primitives/title/index.js +18 -0
  36. package/dist/primitives/title/styles.css +63 -0
  37. package/dist/primitives/title/types.d.ts +18 -0
  38. package/dist/primitives/title/types.js +0 -0
  39. package/dist/ticket-order-card/index.d.ts +20 -1
  40. package/dist/types.d.ts +1 -0
  41. package/package.json +6 -1
  42. package/dist/components/action-bar/index.d.ts +0 -31
  43. package/dist/components/action-bar/index.js +0 -15
  44. package/dist/components/action-bar/styles.css +0 -27
  45. package/dist/components/title-bar/index.d.ts +0 -11
  46. package/dist/components/title-bar/index.js +0 -33
  47. package/dist/components/title-bar/styles.css +0 -66
package/README.md CHANGED
@@ -1,11 +1,10 @@
1
1
  # @doubao-dev/template
2
2
 
3
- Doubao Apps Widget 模板库,用于快速开发卡片类 widget。模板把标题栏、内容区、信息区和操作区组合成稳定的卡片结构,开发者在 `defineWidget` 里选择模板并传入业务数据即可。
3
+ Doubao Apps Widget 模板库,用于快速开发卡片类 widget。模板把标题栏、内容区、信息区和操作区组合成稳定的卡片结构,开发者在 Widget 函数组件里选择模板并传入业务数据即可。
4
4
 
5
5
  推荐一个 widget 直接返回一张模板卡片,不额外包裹无意义的 `view` / `scroll-view`:
6
6
 
7
7
  ```tsx
8
- import { defineWidget } from '@doubao-dev/framework';
9
8
  import { ContentCard, type ContentCardItem } from '@doubao-dev/template';
10
9
 
11
10
  const items: ContentCardItem[] = [
@@ -13,33 +12,32 @@ const items: ContentCardItem[] = [
13
12
  key: 'a',
14
13
  title: '主标题',
15
14
  subtitle: '4199 元 | 商品描述',
16
- thumbnail: <image src="https://example.com/product.png" mode="aspectFill" />
15
+ description: '补充描述',
16
+ thumbnailSrc: 'https://example.com/product.png'
17
17
  }
18
18
  ];
19
19
 
20
- export default defineWidget({
21
- render() {
22
- return (
23
- <ContentCard
24
- items={items}
25
- header={{
26
- actionText: '更多',
27
- onActionClick: () => console.log('more')
28
- }}
29
- footer={{
30
- secondaryActionButton: {
31
- text: '查看更多',
32
- onClick: () => console.log('view more')
33
- },
34
- primaryActionButton: {
35
- text: '立即下单',
36
- onClick: () => console.log('submit')
37
- }
38
- }}
39
- />
40
- );
41
- }
42
- });
20
+ export default function ProductCard() {
21
+ return (
22
+ <ContentCard
23
+ items={items}
24
+ header={{
25
+ actionText: '更多',
26
+ onActionClick: () => console.log('more')
27
+ }}
28
+ footer={{
29
+ secondaryActionButton: {
30
+ text: '查看更多',
31
+ onClick: () => console.log('view more')
32
+ },
33
+ primaryActionButton: {
34
+ text: '立即下单',
35
+ onClick: () => console.log('submit')
36
+ }
37
+ }}
38
+ />
39
+ );
40
+ }
43
41
  ```
44
42
 
45
43
  ## 通用约定
@@ -47,13 +45,80 @@ export default defineWidget({
47
45
  - `header` 配置标题栏右侧操作,类型为 `TemplateCardHeaderProps`,包含 `actionText` / `showAction` / `onActionClick`。
48
46
  - `footer` 配置底部操作区,类型为 `TemplateCardFooterProps`,包含 `primaryActionButton` / `secondaryActionButton`。只传一个按钮时占据整行。
49
47
  - `header`、`footer` 和内容区相互独立。footer 不会影响 `items` 展示数量,内容区也不负责渲染 header/footer。
50
- - `children` 只接管内容区;模板仍保留卡片外壳、header 和 footer。
48
+ - 优先使用模板公开的结构化 Props;部分模板保留的 `children` 已废弃,只用于兼容旧代码。
51
49
  - `className` / `style` / `onClick` 作用在卡片根节点。
52
- - 样式使用组件根 class 内声明的 `--doubao-*` CSS 变量,不把模板变量挂到 `:root`。
50
+ - 模板和 primitives 不声明或暴露组件级 CSS 变量;颜色可以直接消费已有的全局主题 token。
51
+
52
+ ## Primitives
53
+
54
+ `primitives` 是模板体系中的可组合业务单元,不包含完整卡片外壳。`Title`、`Tag`、`Price`、后续的 `Play` 等单元统一通过该子入口导入。
55
+
56
+ ### Title
57
+
58
+ 标题展示组件,默认单行省略,可显式切换为最多两行,并可控制右侧箭头。
59
+
60
+ ```tsx
61
+ import { Title } from '@doubao-dev/template/primitives';
62
+
63
+ <Title text="默认单行标题" />
64
+
65
+ <Title text="需要展示更多信息的两行标题" maxLines={2} />
66
+
67
+ <Title text="不展示箭头的标题" showArrow={false} />
68
+ ```
69
+
70
+ 说明:
71
+
72
+ - `text` 支持 `string | number`。
73
+ - `maxLines` 支持 `1 | 2`,默认是 `1`;超出对应行数后省略。
74
+ - `showArrow` 默认是 `true`。
75
+ - `loading` 展示最大宽度为 40% 的扫光占位。
76
+ - 组件宽度由外部业务容器决定。
77
+
78
+ ### Tag
79
+
80
+ 标签展示组件,用于突出营销、权威或常规辅助信息。标签固定高度为 20px;多个标签横向排列时,标签间距为 4px,容器高度为 26px。
81
+
82
+ ```tsx
83
+ import { Tag } from '@doubao-dev/template/primitives';
84
+
85
+ <Tag text="营销标签" tone="marketing" />
86
+
87
+ <Tag text="权威标签" />
88
+
89
+ <Tag text="普通标签" tone="neutral" />
90
+ ```
91
+
92
+ 说明:
93
+
94
+ - `text` 支持 `string | number`。
95
+ - `tone` 支持 `marketing | authority | neutral`,默认是 `authority`。
96
+ - 标签内容不会收缩或截断;业务容器负责测量或通过换行裁切隐藏放不下的整枚标签,不能只裁切标签文字。
97
+ - `className` 和 `style` 可用于样式定制。
98
+
99
+ ### Price
100
+
101
+ 价格展示组件,用于卡片内容中的金额、币种、前后缀、辅助信息和原价排版。`Price` 是业务展示组件,不包含卡片外壳。
102
+
103
+ ```tsx
104
+ import { Price } from '@doubao-dev/template/primitives';
105
+
106
+ <Price value="1636.92" prefix="均" suffix="券后价" description="销量100+" />
107
+
108
+ <Price value={1636.92} tone="highlight" originalPrice={2000} />
109
+ ```
110
+
111
+ 说明:
112
+
113
+ - `value` 支持 `string | number`;需要保留前导零或末尾小数位时传字符串。
114
+ - `currency` 默认是 `¥`,传空字符串可隐藏币种符号。
115
+ - `tone="highlight"` 使用营销强调色;默认色跟随当前主题。
116
+ - `loading` 展示最大宽度为 60% 的扫光占位。
117
+ - `className` 和 `style` 可用于样式定制。
53
118
 
54
119
  ## ContentCard
55
120
 
56
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/content-card.png" alt="ContentCard 示例" width="400" />
121
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/content-card.png" alt="ContentCard 示例" width="400" />
57
122
 
58
123
  通用列表卡,由标题栏、列表内容和底部操作区组成,适合商品、服务、内容入口等可重复条目。
59
124
 
@@ -65,7 +130,8 @@ import { ContentCard } from '@doubao-dev/template';
65
130
  {
66
131
  title: '蓝牙降噪耳机 Pro',
67
132
  subtitle: '799 元 | 主动降噪 | 现货',
68
- thumbnail: <image src="https://example.com/product.png" mode="aspectFill" />,
133
+ description: '支持空间音频与多设备连接',
134
+ thumbnailSrc: 'https://example.com/product.png',
69
135
  action: {
70
136
  type: 'button',
71
137
  text: '下单',
@@ -87,14 +153,14 @@ import { ContentCard } from '@doubao-dev/template';
87
153
 
88
154
  说明:
89
155
 
90
- - `title` / `subtitle` 支持字符串、数字或自定义节点。字符串和数字使用模板默认单行样式。
91
- - `thumbnail` 是列表项左侧缩略视觉内容,可传图片、图标或自定义节点。
156
+ - `title` / `subtitle` / `description` 支持字符串、数字或自定义节点。字符串和数字使用模板默认样式且最多一行;需要两行标题时传入 `<Title maxLines={2} />`。各行高度由内容决定,行间距为 2px。
157
+ - `thumbnailSrc` 是列表项左侧缩略图地址;`thumbnail` 可传原生媒体节点或 primitive,同时传入时 `thumbnail` 优先。
92
158
  - `action` 是列表项右侧操作区,支持 `{ type: 'button' }`、`{ type: 'switch' }`、`{ type: 'playback' }` 或自定义节点。
93
- - `children` 会替换列表内容区,不包含 header/footer。
159
+ - 标题、描述、缩略内容和操作区分别使用对应的明确字段配置。卡片和列表项的 `children` 已废弃,只用于兼容旧代码。
94
160
 
95
161
  ## PriceActionCard
96
162
 
97
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/price-action-card.png" alt="PriceActionCard 示例" width="400" />
163
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/price-action-card.png" alt="PriceActionCard 示例" width="400" />
98
164
 
99
165
  价格操作卡,由多条"价格 + 信息 + 右侧按钮"组成,适合报价、权益、服务方案等结果列表。
100
166
 
@@ -127,7 +193,7 @@ import { PriceActionCard } from '@doubao-dev/template';
127
193
 
128
194
  ## CheckoutCard
129
195
 
130
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/checkout-card.png" alt="CheckoutCard 示例" width="400" />
196
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/checkout-card.png" alt="CheckoutCard 示例" width="400" />
131
197
 
132
198
  通用提单卡,由商品摘要、提单信息、费用汇总和底部操作区组成,适合下单确认、支付确认等场景。
133
199
 
@@ -138,6 +204,8 @@ import { CheckoutCard } from '@doubao-dev/template';
138
204
  productItems={[
139
205
  {
140
206
  title: '双人下午茶套餐',
207
+ tagText: '新',
208
+ tagTone: 'highlight',
141
209
  spec: '含 2 杯饮品 + 2 款甜点',
142
210
  price: '¥68',
143
211
  quantity: '1'
@@ -155,10 +223,10 @@ import { CheckoutCard } from '@doubao-dev/template';
155
223
  { label: '预计时间', value: '今天 15:30', valueExtra: '请到店自取' }
156
224
  ]}
157
225
  feeItems={[
226
+ { label: '优惠', value: '-¥12' },
158
227
  { label: '包装费', value: '¥2' },
159
228
  { label: '服务费', value: '¥0' }
160
229
  ]}
161
- discountText="共优惠 ¥12"
162
230
  totalPrice="¥126"
163
231
  footer={{
164
232
  secondaryActionButton: { text: '修改' },
@@ -169,14 +237,16 @@ import { CheckoutCard } from '@doubao-dev/template';
169
237
 
170
238
  说明:
171
239
 
172
- - `productItems` 支持单个或多个商品。单项字段复用商品摘要能力,包括 `title`、`spec`、`imageSrc`、`image`、`pricePrefix`、`price`、`quantity`、`showChevron`、`onClick`。
173
- - `infoItems` 配置提单信息列表。
174
- - `feeItems`、`discountText`、`totalLabel`、`totalPrice` 配置费用汇总。
240
+ - `productItems` 支持单个或多个商品。单项字段复用商品摘要能力,包括 `title`、`tagText`、`tagTone`、`spec`、`imageSrc`、`image`、`pricePrefix`、`price`、`quantity`、`showChevron`、`onClick`;`tagText` 展示在标题前,`tagTone="highlight"` 使用强调色。
241
+ - `infoItems` 配置提单信息列表;`value` 和 `valueExtra` 均为 `TemplateText`,使用相同样式并与左侧 label 的首行对齐。
242
+ - `feeItems`、`totalLabel`、`totalPrice` 作为一个整体配置费用汇总,最多展示两行;优惠信息也通过 `feeItems` 传入。
175
243
  - 底部按钮使用 `footer.primaryActionButton` / `footer.secondaryActionButton`。
176
244
 
177
245
  ## TicketOrderCard
178
246
 
179
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/ticket-checkout-card.png" alt="TicketOrderCard 示例" width="400" />
247
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/ticket-checkout-card.png" alt="TicketOrderCard 示例" width="400" />
248
+
249
+ > **已废弃:** 请使用 `CheckoutCard`。`TicketOrderCard` 仅为兼容现有代码保留。
180
250
 
181
251
  票务提单卡,由航程信息、订单信息、费用汇总和底部操作区组成,适合机票、火车票等出行确认场景。
182
252
 
@@ -187,6 +257,8 @@ import { TicketOrderCard } from '@doubao-dev/template';
187
257
  routeItems={[
188
258
  {
189
259
  title: '北京 - 上海',
260
+ tagText: '直飞',
261
+ tagTone: 'highlight',
190
262
  description: '7月1日 周三 08:00-10:10 MU5101 经济舱'
191
263
  },
192
264
  {
@@ -213,13 +285,13 @@ import { TicketOrderCard } from '@doubao-dev/template';
213
285
 
214
286
  说明:
215
287
 
216
- - `routeItems` 配置航程列表,单项可通过 `showChevron={false}` 隐藏标题右侧箭头。
288
+ - `routeItems` 配置航程列表,`tagText` 展示在标题前,`tagTone="highlight"` 使用强调色,单项可通过 `showChevron={false}` 隐藏标题右侧箭头。
217
289
  - `totalContent` 可以接管合计区域,优先级高于 `totalLabel` / `totalPrice`。
218
290
  - 底部按钮使用通用 `footer`。
219
291
 
220
292
  ## TransitCard
221
293
 
222
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/transit-card.png" alt="TransitCard 示例" width="400" />
294
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/transit-card.png" alt="TransitCard 示例" width="400" />
223
295
 
224
296
  交通票务列表卡,由多条出发、中转、到达和价格信息组成,适合展示机票、火车票、大巴等推荐列表。
225
297
 
@@ -255,7 +327,7 @@ import { TransitCard } from '@doubao-dev/template';
255
327
 
256
328
  ## AskHumanCard
257
329
 
258
- <img src="https://unpkg.com/@doubao-dev/template@0.0.38/assets/ask-human-card.png" alt="AskHumanCard 示例" width="400" />
330
+ <img src="https://unpkg.com/@doubao-dev/template@0.0.39/assets/ask-human-card.png" alt="AskHumanCard 示例" width="400" />
259
331
 
260
332
  人工确认选项卡,由一组选项组成,适合让用户在多个候选项中确认选择。
261
333
 
@@ -278,5 +350,5 @@ import { AskHumanCard } from '@doubao-dev/template';
278
350
 
279
351
  说明:
280
352
 
281
- - `items` 配置选项列表,每项可用 `text` 或 `children`。
353
+ - `items` 配置选项列表,选项文案通过 `text` 设置。
282
354
  - `items[].showArrow` 支持 `'left'`、`'right'`、`'none'`,默认不展示;跳过、固定跳转等入口也通过 `items` 表达。
@@ -1,5 +1,5 @@
1
1
  import type { CSSProperties, TouchEvent } from '@lynx-js/types';
2
- import { type ReactElement, type ReactNode } from 'react';
2
+ import { type ReactElement } from 'react';
3
3
  import type { TemplateKey, TemplateText } from '../types.js';
4
4
  import './styles.scss';
5
5
  /** 卡片行为语义。 */
@@ -27,8 +27,6 @@ export interface AskHumanCardItem {
27
27
  key?: TemplateKey;
28
28
  /** 选项文案。 */
29
29
  text?: TemplateText;
30
- /** 选项自定义内容;优先级高于 text。 */
31
- children?: ReactNode;
32
30
  /** 是否显示加载状态;为 true 时选项内展示 loading 动画并禁用点击。 */
33
31
  loading?: boolean;
34
32
  /** 箭头展示位置;默认 `'none'` 不展示。 */
@@ -23,15 +23,11 @@ function getAskHumanCardArrowIconContent(color) {
23
23
  function isAskHumanCardTextEmpty(content) {
24
24
  return null == content || '' === content;
25
25
  }
26
- function isAskHumanCardNodeEmpty(content) {
27
- return null == content || 'boolean' == typeof content || '' === content;
28
- }
29
26
  function renderAskHumanCardItem({ item, index, optionBg, arrowColor }) {
30
27
  const isLoading = true === item.loading;
31
- const hasCustomContent = !isAskHumanCardNodeEmpty(item.children);
32
28
  const arrowPosition = 'left' === item.showArrow || 'right' === item.showArrow ? item.showArrow : void 0;
33
29
  const arrowNode = arrowPosition ? <svg className="doubao-ask-human-card__option-icon" content={getAskHumanCardArrowIconContent(arrowColor)}/> : null;
34
- if (!isLoading && !hasCustomContent && isAskHumanCardTextEmpty(item.text)) return null;
30
+ if (!isLoading && isAskHumanCardTextEmpty(item.text)) return null;
35
31
  return <Button key={item.key ?? index} className={clsx('doubao-ask-human-card__option', {
36
32
  'doubao-ask-human-card__option--with-arrow': null != arrowPosition
37
33
  }, item.className)} style={{
@@ -42,9 +38,9 @@ function renderAskHumanCardItem({ item, index, optionBg, arrowColor }) {
42
38
  item.onClick?.(event);
43
39
  }}>
44
40
  {'left' === arrowPosition && arrowNode}
45
- {hasCustomContent ? item.children : <text className="doubao-ask-human-card__option-text" text-maxline="1">
46
- {item.text}
47
- </text>}
41
+ <text className="doubao-ask-human-card__option-text" text-maxline="1">
42
+ {item.text}
43
+ </text>
48
44
  {'right' === arrowPosition && arrowNode}
49
45
  </Button>;
50
46
  }
@@ -1,5 +1,5 @@
1
1
  import type { CSSProperties, TouchEvent } from '@lynx-js/types';
2
- import type { ReactElement, ReactNode } from 'react';
2
+ import type { ReactElement } from 'react';
3
3
  import { type TemplateCardFooterProps } from '../components/footer/index.js';
4
4
  import { type TemplateCardHeaderProps } from '../components/header/index.js';
5
5
  import { type TemplateProductSummaryProps } from '../components/product-summary/index.js';
@@ -14,8 +14,6 @@ export interface CheckoutCardProps {
14
14
  infoItems?: CheckoutCardInfoItem[];
15
15
  /** 费用明细列表。 */
16
16
  feeItems?: CheckoutCardFeeItem[];
17
- /** 优惠信息文案;传空字符串时不展示。 */
18
- discountText?: string;
19
17
  /** 合计价格前的标签文案。 */
20
18
  totalLabel?: string;
21
19
  /** 合计价格。 */
@@ -39,9 +37,9 @@ export interface CheckoutCardInfoItem {
39
37
  /** 信息项标签。 */
40
38
  label: string;
41
39
  /** 信息项主内容。 */
42
- value: ReactNode;
40
+ value: TemplateText;
43
41
  /** 信息项补充内容。 */
44
- valueExtra?: ReactNode;
42
+ valueExtra?: TemplateText;
45
43
  }
46
44
  export interface CheckoutCardFeeItem {
47
45
  /** 费用项唯一标识;不传时使用数组下标。 */
@@ -22,7 +22,7 @@ function renderCheckoutCardProductItem({ item, index }) {
22
22
  </view>;
23
23
  }
24
24
  function CheckoutCard(props) {
25
- const { header, productItems = [], infoItems = [], feeItems = [], discountText, totalLabel, totalPrice, footer, className, style, onClick } = props;
25
+ const { header, productItems = [], infoItems = [], feeItems = [], totalLabel, totalPrice, footer, className, style, onClick } = props;
26
26
  const productNodes = productItems.map((item, index)=>renderCheckoutCardProductItem({
27
27
  item,
28
28
  index
@@ -32,7 +32,7 @@ function CheckoutCard(props) {
32
32
 
33
33
  {productNodes.length > 0 && <view className="doubao-checkout-card__products">{productNodes}</view>}
34
34
 
35
- <TemplateInfoSection infoItems={infoItems} feeItems={feeItems} discountText={discountText} totalLabel={totalLabel} totalPrice={totalPrice} valueLayout="column"/>
35
+ <TemplateInfoSection infoItems={infoItems} feeItems={feeItems} totalLabel={totalLabel} totalPrice={totalPrice} valueLayout="column"/>
36
36
 
37
37
  <TemplateCardFooter secondaryActionButton={footer?.secondaryActionButton} primaryActionButton={footer?.primaryActionButton}/>
38
38
  </view>;
@@ -6,8 +6,6 @@ export interface TemplateInfoSectionProps {
6
6
  infoItems?: TemplateInfoSectionInfoItem[];
7
7
  /** 费用明细列表。 */
8
8
  feeItems?: TemplateInfoSectionFeeItem[];
9
- /** 优惠信息文案。 */
10
- discountText?: TemplateText;
11
9
  /** 合计价格前的标签文案。 */
12
10
  totalLabel?: TemplateText;
13
11
  /** 合计价格。 */
@@ -65,26 +65,23 @@ function renderTemplateInfoSectionTotal({ totalLabel, totalPrice, totalContent }
65
65
  </view>;
66
66
  }
67
67
  function TemplateInfoSection(props) {
68
- const { infoItems = [], feeItems = [], discountText, totalLabel, totalPrice, totalContent, valueLayout = 'row' } = props;
68
+ const { infoItems = [], feeItems = [], totalLabel, totalPrice, totalContent, valueLayout = 'row' } = props;
69
69
  const infoNodes = infoItems.map(renderTemplateInfoSectionInfoItem).filter((node)=>null != node);
70
70
  const feeNodes = feeItems.map(renderTemplateInfoSectionFeeItem).filter((node)=>null != node);
71
- const hasDiscount = !isTemplateInfoSectionTextEmpty(discountText);
72
71
  const totalNode = renderTemplateInfoSectionTotal({
73
72
  totalLabel,
74
73
  totalPrice,
75
74
  totalContent
76
75
  });
77
76
  const hasInfo = infoNodes.length > 0;
78
- const hasSummary = hasDiscount || feeNodes.length > 0 || null != totalNode;
77
+ const hasSummary = feeNodes.length > 0 || null != totalNode;
79
78
  if (!hasInfo && !hasSummary) return null;
80
79
  return <view className={clsx('doubao-template-info-section', 'column' === valueLayout && 'doubao-template-info-section--value-column')}>
81
80
  {hasInfo && <view className="doubao-template-info-section__info">{infoNodes}</view>}
82
81
  {hasSummary && <view className="doubao-template-info-section__summary">
83
82
  <view className="doubao-template-info-section__summary-content">
84
- {hasDiscount && <text className="doubao-template-info-section__fee" text-maxline="1">
85
- {discountText}
86
- </text>}
87
- {feeNodes}
83
+ {}
84
+ {feeNodes.length > 0 && <view className="doubao-template-info-section__fees">{feeNodes}</view>}
88
85
  {totalNode}
89
86
  </view>
90
87
  </view>}
@@ -15,6 +15,7 @@
15
15
 
16
16
  .doubao-template-info-section__info-row {
17
17
  flex-direction: row;
18
+ align-items: flex-start;
18
19
  width: 100%;
19
20
  display: flex;
20
21
  }
@@ -62,7 +63,7 @@
62
63
 
63
64
  .doubao-template-info-section__info-value {
64
65
  min-width: 0;
65
- color: var(--neutral-50);
66
+ color: var(--neutral-70);
66
67
  text-align: right;
67
68
  margin-left: 6px;
68
69
  font-size: 13px;
@@ -93,9 +94,24 @@
93
94
  }
94
95
 
95
96
  .doubao-template-info-section__summary-content {
96
- flex-direction: row;
97
+ flex-flow: wrap;
98
+ flex: 1 1 0;
97
99
  justify-content: flex-end;
98
100
  align-items: center;
101
+ gap: 0 6px;
102
+ min-width: 0;
103
+ max-width: 100%;
104
+ max-height: 42px;
105
+ display: flex;
106
+ overflow: hidden;
107
+ }
108
+
109
+ .doubao-template-info-section__fees {
110
+ flex-direction: row;
111
+ flex: 0 auto;
112
+ align-items: center;
113
+ gap: 0 6px;
114
+ min-width: 0;
99
115
  max-width: 100%;
100
116
  display: flex;
101
117
  overflow: hidden;
@@ -103,46 +119,40 @@
103
119
 
104
120
  .doubao-template-info-section__fee {
105
121
  min-width: 0;
106
- color: var(--neutral-50);
107
- flex-shrink: 1;
108
- margin-left: 6px;
122
+ max-width: 100%;
123
+ color: var(--neutral-70);
124
+ flex: 0 auto;
109
125
  font-size: 13px;
110
126
  font-weight: 400;
111
127
  line-height: 20px;
112
128
  }
113
129
 
114
- .doubao-template-info-section__fee:first-child {
115
- margin-left: 0;
116
- }
117
-
118
130
  .doubao-template-info-section__total {
119
- flex-flow: wrap;
120
- flex-shrink: 0;
131
+ flex-direction: row;
132
+ flex: 0 auto;
121
133
  justify-content: flex-end;
122
134
  align-items: center;
123
135
  min-width: 0;
124
136
  max-width: 100%;
125
- margin-left: 6px;
126
137
  display: flex;
127
- }
128
-
129
- .doubao-template-info-section__total:first-child {
130
- margin-left: 0;
138
+ overflow: hidden;
131
139
  }
132
140
 
133
141
  .doubao-template-info-section__total-label {
142
+ min-width: 0;
134
143
  max-width: 100%;
135
144
  color: var(--neutral-100);
136
- flex-shrink: 0;
145
+ flex: 0 auto;
137
146
  font-size: 13px;
138
147
  font-weight: 500;
139
148
  line-height: 20px;
140
149
  }
141
150
 
142
151
  .doubao-template-info-section__total-price {
152
+ min-width: 0;
143
153
  max-width: 100%;
144
154
  color: var(--neutral-100);
145
- flex-shrink: 0;
155
+ flex: 0 auto;
146
156
  margin-left: 2px;
147
157
  font-size: 15px;
148
158
  font-weight: 500;
@@ -1,10 +1,14 @@
1
1
  import type { TouchEvent } from '@lynx-js/types';
2
2
  import type { ReactElement, ReactNode } from 'react';
3
- import type { TemplateText } from '../../types.js';
3
+ import type { TemplateTagTone, TemplateText } from '../../types.js';
4
4
  import './styles.scss';
5
5
  export interface TemplateProductSummaryProps {
6
6
  /** 商品标题。 */
7
7
  title?: string;
8
+ /** 商品标题前的标签文案。 */
9
+ tagText?: TemplateText;
10
+ /** 商品标题标签的色调。 */
11
+ tagTone?: TemplateTagTone;
8
12
  /** 商品规格说明。 */
9
13
  spec?: string;
10
14
  /** 商品图片地址。 */
@@ -1,4 +1,4 @@
1
- import { TemplateChevronRightIcon } from "../chevron-right-icon/index.js";
1
+ import { Title } from "../../primitives/title/index.js";
2
2
  import "./styles.css";
3
3
  function isTemplateProductSummaryTextEmpty(content) {
4
4
  return null == content || '' === content;
@@ -13,9 +13,11 @@ function TemplateProductSummaryImage(props) {
13
13
  return <view className="doubao-template-product-summary__image"/>;
14
14
  }
15
15
  function TemplateProductSummary(props) {
16
- const { title, spec, imageSrc, image: image1, pricePrefix, price, quantity, showChevron = true, onClick } = props;
16
+ const { title, tagText, tagTone = 'default', spec, imageSrc, image: image1, pricePrefix, price, quantity, showChevron = true, onClick } = props;
17
17
  const hasImage = !isTemplateProductSummaryNodeEmpty(image1) || null != imageSrc && imageSrc.length > 0;
18
- const hasTitle = null != title && title.length > 0;
18
+ const titleText = title ?? '';
19
+ const hasTitle = titleText.length > 0;
20
+ const hasTag = !isTemplateProductSummaryTextEmpty(tagText);
19
21
  const hasPrice = null != pricePrefix && pricePrefix.length > 0 || !isTemplateProductSummaryTextEmpty(price);
20
22
  const hasMainLine = hasTitle || hasPrice;
21
23
  const hasSpec = null != spec && spec.length > 0;
@@ -27,10 +29,10 @@ function TemplateProductSummary(props) {
27
29
  <view className={hasImage ? 'doubao-template-product-summary__content doubao-template-product-summary__content--with-image' : 'doubao-template-product-summary__content'}>
28
30
  {hasMainLine && <view className="doubao-template-product-summary__line-main">
29
31
  {hasTitle && <view className="doubao-template-product-summary__title-wrap">
30
- <text className="doubao-template-product-summary__title" text-maxline="1">
31
- {title}
32
- </text>
33
- {showChevron && <TemplateChevronRightIcon className="doubao-template-product-summary__chevron"/>}
32
+ {hasTag && <text className={'highlight' === tagTone ? 'doubao-template-product-summary__tag doubao-template-product-summary__tag--highlight' : 'doubao-template-product-summary__tag'} text-maxline="1">
33
+ {tagText}
34
+ </text>}
35
+ <Title text={titleText} showArrow={showChevron}/>
34
36
  </view>}
35
37
  {hasPrice && <view className="doubao-template-product-summary__price">
36
38
  {null != pricePrefix && pricePrefix.length > 0 && <text className="doubao-template-product-summary__price-prefix" text-maxline="1">
@@ -49,32 +49,41 @@
49
49
  flex-direction: row;
50
50
  flex: 1 1 0;
51
51
  align-items: center;
52
+ gap: 4px;
52
53
  min-width: 0;
53
54
  margin-right: 8px;
54
55
  display: flex;
55
56
  }
56
57
 
57
- .doubao-template-product-summary__title, .doubao-template-product-summary__price-prefix, .doubao-template-product-summary__price-value, .doubao-template-product-summary__spec, .doubao-template-product-summary__quantity {
58
+ .doubao-template-product-summary__tag {
59
+ box-sizing: border-box;
60
+ height: 18px;
61
+ color: var(--neutral-70);
58
62
  text-overflow: ellipsis;
59
63
  white-space: nowrap;
64
+ background-color: var(--bg-base-2-overlay);
65
+ border-radius: 4px;
66
+ flex-shrink: 0;
67
+ padding: 2px 4px;
60
68
  font-family: PingFang SC, sans-serif;
69
+ font-size: 12px;
61
70
  font-style: normal;
71
+ font-weight: 400;
72
+ line-height: 14px;
62
73
  overflow: hidden;
63
74
  }
64
75
 
65
- .doubao-template-product-summary__title {
66
- min-width: 0;
67
- color: var(--neutral-100);
68
- font-size: 16px;
69
- font-weight: 500;
70
- line-height: 24px;
76
+ .doubao-template-product-summary__tag--highlight {
77
+ color: var(--primary-60);
78
+ background-color: var(--primary-transparent-1);
71
79
  }
72
80
 
73
- .doubao-template-product-summary__chevron {
74
- flex-shrink: 0;
75
- width: 12px;
76
- height: 12px;
77
- margin-left: 5px;
81
+ .doubao-template-product-summary__price-prefix, .doubao-template-product-summary__price-value, .doubao-template-product-summary__spec, .doubao-template-product-summary__quantity {
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ font-family: PingFang SC, sans-serif;
85
+ font-style: normal;
86
+ overflow: hidden;
78
87
  }
79
88
 
80
89
  .doubao-template-product-summary__price {
@@ -103,15 +112,15 @@
103
112
 
104
113
  .doubao-template-product-summary__spec {
105
114
  min-width: 0;
106
- color: var(--neutral-50);
115
+ color: var(--neutral-70);
107
116
  flex: 1 1 0;
108
- font-size: 12px;
117
+ font-size: 13px;
109
118
  font-weight: 400;
110
- line-height: 18px;
119
+ line-height: 20px;
111
120
  }
112
121
 
113
122
  .doubao-template-product-summary__quantity {
114
- color: var(--neutral-50);
123
+ color: var(--neutral-70);
115
124
  flex-shrink: 0;
116
125
  margin-left: 8px;
117
126
  font-size: 13px;
@@ -1,6 +1,6 @@
1
1
  import type { TouchEvent } from '@lynx-js/types';
2
2
  import type { ReactElement } from 'react';
3
- import type { TemplateKey, TemplateText } from '../../types.js';
3
+ import type { TemplateKey, TemplateTagTone, TemplateText } from '../../types.js';
4
4
  import './styles.scss';
5
5
  export interface TemplateRouteSectionProps {
6
6
  /** 航程信息列表。 */
@@ -13,6 +13,10 @@ export interface TemplateRouteSectionItem {
13
13
  title?: TemplateText;
14
14
  /** 航程描述,例如「6月1日 周六 20:50-23:50 CZ0000 经济舱」。 */
15
15
  description?: TemplateText;
16
+ /** 航程标题前的标签文案。 */
17
+ tagText?: TemplateText;
18
+ /** 航程标题标签的色调。 */
19
+ tagTone?: TemplateTagTone;
16
20
  /** 是否展示标题右侧箭头。 */
17
21
  showChevron?: boolean;
18
22
  /** 点击航程项时触发。 */