@anker-in/campaign-ui 0.3.2 → 0.3.4

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 (229) hide show
  1. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
  2. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
  3. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  4. package/dist/cjs/components/LiveChatWidget/api/chat.d.ts +23 -2
  5. package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
  6. package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
  7. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js +1 -1
  8. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  9. package/dist/cjs/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
  10. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js +1 -1
  11. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  12. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +2 -2
  13. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  14. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
  15. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
  16. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  17. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
  18. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
  19. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
  20. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  21. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  22. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
  23. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
  24. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  25. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
  26. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
  27. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
  28. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
  29. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
  30. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  31. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
  32. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  33. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  34. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  35. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  36. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
  37. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
  38. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
  39. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  40. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  41. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  42. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  43. package/dist/cjs/components/LiveChatWidget/components/MessageContent.js +1 -1
  44. package/dist/cjs/components/LiveChatWidget/components/MessageContent.js.map +2 -2
  45. package/dist/cjs/components/LiveChatWidget/components/MessageList.js +2 -2
  46. package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +2 -2
  47. package/dist/cjs/components/LiveChatWidget/constants.d.ts +5 -0
  48. package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
  49. package/dist/cjs/components/LiveChatWidget/constants.js.map +3 -3
  50. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
  51. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  52. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  53. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +35 -2
  54. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
  55. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  56. package/dist/cjs/components/LiveChatWidget/index.d.ts +1 -1
  57. package/dist/cjs/components/LiveChatWidget/index.js +1 -1
  58. package/dist/cjs/components/LiveChatWidget/index.js.map +2 -2
  59. package/dist/cjs/components/LiveChatWidget/types.d.ts +212 -3
  60. package/dist/cjs/components/LiveChatWidget/types.js +1 -1
  61. package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
  62. package/dist/cjs/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
  63. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +2 -0
  64. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +7 -0
  65. package/dist/cjs/components/chat/markdown.js +1 -1
  66. package/dist/cjs/components/chat/markdown.js.map +2 -2
  67. package/dist/cjs/components/credits/creditsBanner/index.js +2 -2
  68. package/dist/cjs/components/credits/creditsBanner/index.js.map +2 -2
  69. package/dist/cjs/components/index.d.ts +2 -0
  70. package/dist/cjs/components/index.js +1 -1
  71. package/dist/cjs/components/index.js.map +3 -3
  72. package/dist/cjs/stories/LiveChatWidget.stories.d.ts +1 -79
  73. package/dist/cjs/stories/LiveChatWidget.stories.js +8 -47
  74. package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
  75. package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
  76. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
  77. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  78. package/dist/esm/components/LiveChatWidget/api/chat.d.ts +23 -2
  79. package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
  80. package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
  81. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
  82. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  83. package/dist/esm/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
  84. package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
  85. package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  86. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
  87. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  88. package/dist/esm/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
  89. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
  90. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  91. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
  92. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
  93. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
  94. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  95. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  96. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
  97. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
  98. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  99. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
  100. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
  101. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
  102. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
  103. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
  104. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  105. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
  106. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  107. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  108. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  109. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  110. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
  111. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
  112. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
  113. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  114. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  115. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  116. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  117. package/dist/esm/components/LiveChatWidget/components/MessageContent.js +1 -1
  118. package/dist/esm/components/LiveChatWidget/components/MessageContent.js.map +2 -2
  119. package/dist/esm/components/LiveChatWidget/components/MessageList.js +2 -2
  120. package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +2 -2
  121. package/dist/esm/components/LiveChatWidget/constants.d.ts +5 -0
  122. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  123. package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
  124. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
  125. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  126. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  127. package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +35 -2
  128. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
  129. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  130. package/dist/esm/components/LiveChatWidget/index.d.ts +1 -1
  131. package/dist/esm/components/LiveChatWidget/index.js +1 -1
  132. package/dist/esm/components/LiveChatWidget/index.js.map +2 -2
  133. package/dist/esm/components/LiveChatWidget/types.d.ts +212 -3
  134. package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
  135. package/dist/esm/components/LiveChatWidget/utils/fetcher.js +2 -0
  136. package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +7 -0
  137. package/dist/esm/components/chat/markdown.js +1 -1
  138. package/dist/esm/components/chat/markdown.js.map +2 -2
  139. package/dist/esm/components/credits/creditsBanner/index.js +2 -2
  140. package/dist/esm/components/credits/creditsBanner/index.js.map +2 -2
  141. package/dist/esm/components/index.d.ts +2 -0
  142. package/dist/esm/components/index.js +1 -1
  143. package/dist/esm/components/index.js.map +3 -3
  144. package/dist/esm/stories/LiveChatWidget.stories.d.ts +1 -79
  145. package/dist/esm/stories/LiveChatWidget.stories.js +8 -47
  146. package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
  147. package/dist/index.d.mts +1305 -0
  148. package/dist/index.d.ts +1305 -0
  149. package/dist/index.js +26656 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/index.mjs +26641 -0
  152. package/dist/index.mjs.map +1 -0
  153. package/package.json +8 -1
  154. package/src/components/LiveChatWidget/LiveChatWidget.tsx +887 -0
  155. package/src/components/LiveChatWidget/api/chat.ts +175 -0
  156. package/src/components/LiveChatWidget/components/ChatBubble.tsx +152 -0
  157. package/src/components/LiveChatWidget/components/ChatHeader.tsx +150 -0
  158. package/src/components/LiveChatWidget/components/ChatInput.tsx +253 -0
  159. package/src/components/LiveChatWidget/components/ChatMessage.tsx +190 -0
  160. package/src/components/LiveChatWidget/components/ChatWindow.tsx +363 -0
  161. package/src/components/LiveChatWidget/components/ComplianceDialog.tsx +216 -0
  162. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +202 -0
  163. package/src/components/LiveChatWidget/components/MessageContent/ErrorBlock.tsx +75 -0
  164. package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +128 -0
  165. package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +152 -0
  166. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +227 -0
  167. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +377 -0
  168. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +293 -0
  169. package/src/components/LiveChatWidget/components/MessageContent/PromotionList.tsx +170 -0
  170. package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +91 -0
  171. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +110 -0
  172. package/src/components/LiveChatWidget/components/MessageContent/ThinkingBlock.tsx +53 -0
  173. package/src/components/LiveChatWidget/components/MessageContent/index.ts +16 -0
  174. package/src/components/LiveChatWidget/components/MessageContent.tsx +113 -0
  175. package/src/components/LiveChatWidget/components/MessageList.tsx +261 -0
  176. package/src/components/LiveChatWidget/components/ScrollAnchor.tsx +75 -0
  177. package/src/components/LiveChatWidget/constants.ts +36 -0
  178. package/src/components/LiveChatWidget/hooks/useChatAPI.ts +146 -0
  179. package/src/components/LiveChatWidget/hooks/useChatState.ts +1090 -0
  180. package/src/components/LiveChatWidget/hooks/useSession.ts +123 -0
  181. package/src/components/LiveChatWidget/index.tsx +63 -0
  182. package/src/components/LiveChatWidget/types.ts +1011 -0
  183. package/src/components/LiveChatWidget/utils/cartTransformers.ts +72 -0
  184. package/src/components/LiveChatWidget/utils/fetcher.ts +131 -0
  185. package/src/components/LiveChatWidget/utils/messageRenderers.ts +120 -0
  186. package/src/components/LiveChatWidget/utils/productTransformers.ts +149 -0
  187. package/src/components/LiveChatWidget/utils/userId.ts +140 -0
  188. package/src/components/LiveChatWidget/utils/validation.ts +99 -0
  189. package/src/components/chat/markdown.tsx +1 -1
  190. package/src/components/credits/creditsBanner/index.tsx +5 -5
  191. package/src/components/index.ts +23 -0
  192. package/src/stories/LiveChatWidget.stories.tsx +322 -0
  193. package/src/styles/livechat.css +317 -0
  194. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  195. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  196. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  197. package/dist/cjs/components/credits/context/utils/atobID.d.ts +0 -1
  198. package/dist/cjs/components/credits/context/utils/atobID.js +0 -2
  199. package/dist/cjs/components/credits/context/utils/atobID.js.map +0 -7
  200. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  201. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  202. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  203. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  204. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  205. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  206. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  207. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  208. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  209. package/dist/cjs/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  210. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js +0 -2
  211. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js.map +0 -7
  212. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  213. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  214. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  215. package/dist/esm/components/credits/context/utils/atobID.d.ts +0 -1
  216. package/dist/esm/components/credits/context/utils/atobID.js +0 -2
  217. package/dist/esm/components/credits/context/utils/atobID.js.map +0 -7
  218. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  219. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  220. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  221. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  222. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  223. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  224. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  225. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  226. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  227. package/dist/esm/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  228. package/dist/esm/components/credits/context/utils/variantGetCoupon.js +0 -2
  229. package/dist/esm/components/credits/context/utils/variantGetCoupon.js.map +0 -7
@@ -0,0 +1,202 @@
1
+ /**
2
+ * 购物车卡片渲染器
3
+ * 显示购物车内容、价格汇总和结账按钮
4
+ * 基于后端返回的购物车数据结构
5
+ */
6
+
7
+ import React from 'react'
8
+ import type { MessageRenderer, CartContent, CartLine, CartAmount } from '../../types'
9
+ import { CURRENCY_SYMBOLS, DEFAULT_COMMON_TEXT } from '../../constants.js'
10
+
11
+ /**
12
+ * 格式化金额
13
+ * @param amount 金额对象
14
+ * @returns 格式化后的金额字符串(如 "$99.99")
15
+ */
16
+ function formatAmount(amount: CartAmount): string {
17
+ const { amount: value, currencyCode } = amount
18
+
19
+ const symbol = CURRENCY_SYMBOLS[currencyCode] || currencyCode
20
+ const numValue = parseFloat(value)
21
+
22
+ return `${symbol}${numValue.toFixed(2)}`
23
+ }
24
+
25
+ /**
26
+ * 购物车商品行组件
27
+ */
28
+ const CartLineItem: React.FC<{
29
+ line: CartLine
30
+ }> = ({ line }) => {
31
+ const { quantity, merchandise, cost } = line
32
+ const { product, title: variantTitle, image } = merchandise
33
+
34
+ // 商品图片 URL
35
+ const imageUrl = image?.url || ''
36
+
37
+ // 判断是否有折扣(总价 < 原价)
38
+ const hasDiscount =
39
+ parseFloat(cost.totalAmount.amount) < parseFloat(cost.subtotalAmount.amount) &&
40
+ cost.totalAmount.currencyCode === cost.subtotalAmount.currencyCode
41
+
42
+ return (
43
+ <div className="flex gap-4">
44
+ {/* 商品图片 */}
45
+ <div className="shrink-0 overflow-hidden rounded-md" style={{ width: '72px', height: '72px' }}>
46
+ <img src={imageUrl} alt={product.title} className="size-full object-cover" loading="lazy" />
47
+ </div>
48
+
49
+ {/* 商品信息 */}
50
+ <div className="flex flex-1 flex-col">
51
+ <h4 className="line-clamp-2 text-sm tablet:text-[16px] font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]">
52
+ {product.title}
53
+ </h4>
54
+ <div className="flex items-end justify-between gap-2">
55
+ {/* 左侧:标题、变体、数量 */}
56
+ <div className="flex-1">
57
+ {variantTitle && (
58
+ <p className="mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]">{variantTitle}</p>
59
+ )}
60
+ <p className="mt-1 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]">×{quantity}</p>
61
+ </div>
62
+
63
+ {/* 右侧:价格 */}
64
+ <div className="flex gap-1 text-right">
65
+ <div className="tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-gray-900">
66
+ {formatAmount(cost.totalAmount)}
67
+ </div>
68
+ {hasDiscount && (
69
+ <div className="tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through">
70
+ {formatAmount(cost.subtotalAmount)}
71
+ </div>
72
+ )}
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ )
78
+ }
79
+
80
+ /**
81
+ * 价格汇总组件(简化版)
82
+ */
83
+ const CartSummary: React.FC<{
84
+ total: CartAmount
85
+ totalText: string
86
+ }> = ({ total, totalText }) => {
87
+ return (
88
+ <div className="border-t border-gray-200 p-4">
89
+ <div className="flex items-center justify-between">
90
+ <span className="text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900">{totalText}</span>
91
+ <span className="text-base tablet:text-[18px] font-bold leading-[1.4] tracking-[-0.02em] text-gray-900">
92
+ {formatAmount(total)}
93
+ </span>
94
+ </div>
95
+ </div>
96
+ )
97
+ }
98
+
99
+ /**
100
+ * 购物车卡片渲染器
101
+ *
102
+ * 功能:
103
+ * - 显示购物车商品列表
104
+ * - 显示价格汇总(小计、折扣、总计)
105
+ * - 显示折扣码
106
+ * - 提供 Checkout 按钮
107
+ * - 空购物车状态
108
+ *
109
+ * 布局:
110
+ * ```
111
+ * ┌─────────────────────────────────┐
112
+ * │ 购物车 (3 件商品) │
113
+ * ├─────────────────────────────────┤
114
+ * │ [图] 商品1 │
115
+ * │ 变体: Black │
116
+ * │ 数量: 2 $199.98 │
117
+ * ├─────────────────────────────────┤
118
+ * │ [图] 商品2 │
119
+ * │ 变体: White │
120
+ * │ 数量: 1 $99.99 │
121
+ * ├─────────────────────────────────┤
122
+ * │ 小计 $299.97 │
123
+ * │ 折扣 [SPRING20] -$30.00 │
124
+ * │ 总计 $269.97 │
125
+ * ├─────────────────────────────────┤
126
+ * │ [Checkout 按钮] │
127
+ * └─────────────────────────────────┘
128
+ * ```
129
+ *
130
+ * @example
131
+ * ```tsx
132
+ * const content: CartContent = {
133
+ * type: 'cart',
134
+ * data: {
135
+ * isEmpty: false,
136
+ * cartId: "gid://...",
137
+ * totalQuantity: 3,
138
+ * lines: [...],
139
+ * cost: {...},
140
+ * checkoutUrl: "https://..."
141
+ * }
142
+ * }
143
+ * <CartCard.render(content, false, false) />
144
+ * ```
145
+ */
146
+ export const CartCard: MessageRenderer = {
147
+ render: content => {
148
+ const cartContent = content as CartContent
149
+ const { data } = cartContent
150
+
151
+ if (!data) {
152
+ return null
153
+ }
154
+
155
+ const { isEmpty, lines, cost, checkoutUrl, onCart, cartId, commonText } = data
156
+
157
+ // 合并默认文案和自定义文案
158
+ const mergedText = { ...DEFAULT_COMMON_TEXT, ...commonText }
159
+
160
+ // 处理购物车按钮点击
161
+ const handleCart = () => {
162
+ if (onCart) {
163
+ onCart(cartId, checkoutUrl)
164
+ } else if (checkoutUrl) {
165
+ window.open(checkoutUrl, '_blank', 'noopener,noreferrer')
166
+ }
167
+ }
168
+
169
+ // 空购物车状态 - 不展示组件
170
+ if (isEmpty || !lines || lines.length === 0) {
171
+ return null
172
+ }
173
+
174
+ return (
175
+ <div className="w-full max-w-md overflow-hidden rounded-2xl shadow-sm" style={{ backgroundColor: '#F5F6F7' }}>
176
+ {/* 商品列表 */}
177
+ <div className="flex flex-col gap-6 overflow-y-auto p-4">
178
+ {lines.map(line => (
179
+ <CartLineItem key={line.id} line={line} />
180
+ ))}
181
+ </div>
182
+
183
+ {/* 价格汇总 */}
184
+ <CartSummary total={cost.totalAmount} totalText={mergedText.total} />
185
+
186
+ {/* Checkout 按钮 */}
187
+ {(checkoutUrl || onCart) && (
188
+ <div className="px-4 pb-4">
189
+ <button
190
+ type="button"
191
+ onClick={handleCart}
192
+ className="w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white"
193
+ style={{ backgroundColor: '#1D1D1F' }}
194
+ >
195
+ {mergedText.viewMore}
196
+ </button>
197
+ </div>
198
+ )}
199
+ </div>
200
+ )
201
+ },
202
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 错误消息渲染器
3
+ * 显示错误提示信息
4
+ * 基于 specs/livechat-widget/plan.md 的错误处理设计
5
+ */
6
+
7
+ import React from 'react'
8
+ import type { MessageRenderer, ErrorContent } from '../../types'
9
+
10
+ /**
11
+ * 错误消息渲染器
12
+ *
13
+ * 功能:
14
+ * - 显示错误消息
15
+ * - 红色背景警告样式
16
+ * - 可选错误代码
17
+ *
18
+ * 错误类型:
19
+ * - 网络错误
20
+ * - API 错误
21
+ * - 会话过期
22
+ * - 其他系统错误
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * const content: ErrorContent = {
27
+ * type: 'error',
28
+ * data: {
29
+ * message: '网络连接失败,请重试',
30
+ * code: 'NETWORK_ERROR'
31
+ * }
32
+ * }
33
+ * <ErrorBlock.render(content, false, false) />
34
+ * ```
35
+ */
36
+ export const ErrorBlock: MessageRenderer = {
37
+ render: (content, isUser, isSystem) => {
38
+ const errorContent = content as ErrorContent
39
+ const { message, code } = errorContent.data
40
+
41
+ if (!message) {
42
+ return null
43
+ }
44
+
45
+ return (
46
+ <div className="flex flex-col gap-1 rounded-lg border border-red-200 bg-red-50 px-3 py-2">
47
+ {/* 错误图标 + 消息 */}
48
+ <div className="flex items-start gap-2">
49
+ {/* 错误图标 */}
50
+ <svg
51
+ className="mt-0.5 size-5 shrink-0 text-red-600"
52
+ viewBox="0 0 24 24"
53
+ fill="none"
54
+ stroke="currentColor"
55
+ strokeWidth="2"
56
+ strokeLinecap="round"
57
+ strokeLinejoin="round"
58
+ >
59
+ <circle cx="12" cy="12" r="10" />
60
+ <line x1="12" y1="8" x2="12" y2="12" />
61
+ <line x1="12" y1="16" x2="12.01" y2="16" />
62
+ </svg>
63
+
64
+ {/* 错误消息 */}
65
+ <div className="flex-1">
66
+ <p className="text-sm font-medium text-red-800">{message}</p>
67
+
68
+ {/* 错误代码(可选) */}
69
+ {code && <p className="mt-1 font-mono text-xs text-red-600">Error code: {code}</p>}
70
+ </div>
71
+ </div>
72
+ </div>
73
+ )
74
+ },
75
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * FAQ 列表组件
3
+ * 显示常见问题列表,支持折叠/展开
4
+ */
5
+
6
+ import React, { useState } from 'react'
7
+ import ReactMarkdown from 'react-markdown'
8
+ import remarkGfm from 'remark-gfm'
9
+ import type { FAQListContent, FAQItem, MessageRenderer, MessageContent } from '../../types'
10
+
11
+ export interface FAQListProps {
12
+ content: FAQListContent
13
+ onQuestionClick?: (question: string) => void
14
+ }
15
+
16
+ /**
17
+ * FAQ 列表渲染器
18
+ * 用于注册到 MessageRendererRegistry
19
+ */
20
+ export const FAQListRenderer: MessageRenderer = {
21
+ render: (content: MessageContent) => {
22
+ if (content.type !== 'faq_list') {
23
+ return null
24
+ }
25
+ return <FAQList content={content as FAQListContent} />
26
+ },
27
+ }
28
+
29
+ export const FAQList: React.FC<FAQListProps> = ({ content, onQuestionClick }) => {
30
+ const { found, count, total, results } = content.data
31
+ const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
32
+
33
+ // 如果没有找到结果
34
+ if (!found || results.length === 0) {
35
+ return (
36
+ <div className="rounded-lg border border-gray-200 bg-white p-4">
37
+ <p className="text-sm text-gray-500">未找到相关问题</p>
38
+ </div>
39
+ )
40
+ }
41
+
42
+ // 切换展开/折叠
43
+ const toggleExpand = (id: string) => {
44
+ setExpandedIds(prev => {
45
+ const newSet = new Set(prev)
46
+ if (newSet.has(id)) {
47
+ newSet.delete(id)
48
+ } else {
49
+ newSet.add(id)
50
+ }
51
+ return newSet
52
+ })
53
+ }
54
+
55
+ // 处理相关问题点击
56
+ const handleRelatedQuestionClick = (question: string) => {
57
+ onQuestionClick?.(question)
58
+ }
59
+
60
+ return (
61
+ <div className="space-y-2">
62
+ {/* FAQ 列表 */}
63
+ <div className="space-y-2">
64
+ {results.map(item => {
65
+ const isExpanded = expandedIds.has(item.id)
66
+
67
+ return (
68
+ <div key={item.id} className="overflow-hidden rounded-2xl bg-[#F5F6F7] transition-all">
69
+ {/* 问题标题 - 可点击展开/折叠 */}
70
+ <button
71
+ onClick={() => toggleExpand(item.id)}
72
+ className="flex w-full items-center justify-between gap-3 p-4 text-left transition-colors "
73
+ >
74
+ <div className="flex-1">
75
+ <div className="flex items-center gap-2">
76
+ <span className="text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]">
77
+ {item.question}
78
+ </span>
79
+ </div>
80
+ </div>
81
+
82
+ {/* 展开/折叠图标 */}
83
+ <svg
84
+ className={`size-5 shrink-0 text-[#080A0F] transition-transform ${isExpanded ? 'rotate-180' : ''}`}
85
+ fill="none"
86
+ viewBox="0 0 24 24"
87
+ stroke="currentColor"
88
+ >
89
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
90
+ </svg>
91
+ </button>
92
+
93
+ {/* 答案内容 - 展开时显示 */}
94
+ {isExpanded && (
95
+ <div className=" bg-[#F5F6F7] px-4 py-3 pt-0">
96
+ <div
97
+ className="prose prose-sm max-w-none border-t border-gray-100 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]"
98
+ style={{ paddingTop: '12px' }}
99
+ >
100
+ <ReactMarkdown remarkPlugins={[remarkGfm]}>{item.answer}</ReactMarkdown>
101
+ </div>
102
+
103
+ {/* 相关问题 */}
104
+ {item.relatedQuestions && item.relatedQuestions.length > 0 && (
105
+ <div className="mt-4 border-t border-gray-200 pt-3">
106
+ <p className="mb-2 text-xs font-medium text-gray-500">You may also want to know:</p>
107
+ <div className="space-y-1">
108
+ {item.relatedQuestions.map((q, idx) => (
109
+ <button
110
+ key={idx}
111
+ onClick={() => handleRelatedQuestionClick(q)}
112
+ className="block w-full text-left text-xs text-blue-600 hover:text-blue-700 hover:underline"
113
+ >
114
+ {q}
115
+ </button>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ )}
120
+ </div>
121
+ )}
122
+ </div>
123
+ )
124
+ })}
125
+ </div>
126
+ </div>
127
+ )
128
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 政策说明渲染器
3
+ * 显示退货、保修等政策信息
4
+ * 基于 specs/livechat-widget/data-model.md 的政策数据模型
5
+ */
6
+
7
+ import React, { useState } from 'react'
8
+ import ReactMarkdown from 'react-markdown'
9
+ import remarkGfm from 'remark-gfm'
10
+ import type { MessageRenderer, PolicyContent } from '../../types'
11
+
12
+ /**
13
+ * 政策说明渲染器
14
+ *
15
+ * 功能:
16
+ * - 显示政策标题和内容
17
+ * - 支持 Markdown 格式
18
+ * - 可折叠/展开(内容较长时)
19
+ *
20
+ * 政策类型:
21
+ * - 退货政策
22
+ * - 保修政策
23
+ * - 运费政策
24
+ * - 隐私政策
25
+ *
26
+ * 布局:
27
+ * ```
28
+ * ┌─────────────────────────┐
29
+ * │ 📋 政策标题 │
30
+ * ├─────────────────────────┤
31
+ * │ 政策内容... │
32
+ * │ (支持 Markdown) │
33
+ * │ │
34
+ * │ [展开/收起] │
35
+ * └─────────────────────────┘
36
+ * ```
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * const content: PolicyContent = {
41
+ * type: 'policy',
42
+ * data: {
43
+ * title: '退货政策',
44
+ * content: '我们提供 30 天无理由退货...'
45
+ * }
46
+ * }
47
+ * <PolicyBlock.render(content, false, false) />
48
+ * ```
49
+ */
50
+ export const PolicyBlock: MessageRenderer = {
51
+ render: (content, isUser, isSystem) => {
52
+ const policyContent = content as PolicyContent
53
+ const { title, content: policyText } = policyContent.data
54
+
55
+ if (!title || !policyText) {
56
+ return null
57
+ }
58
+
59
+ // 判断内容是否较长(超过 200 字符)
60
+ const isLongContent = policyText.length > 200
61
+
62
+ return <PolicyCard title={title} content={policyText} isLong={isLongContent} />
63
+ },
64
+ }
65
+
66
+ /**
67
+ * 政策卡片组件(支持折叠)
68
+ */
69
+ const PolicyCard: React.FC<{
70
+ title: string
71
+ content: string
72
+ isLong: boolean
73
+ }> = ({ title, content, isLong }) => {
74
+ const [isExpanded, setIsExpanded] = useState(!isLong)
75
+
76
+ const toggleExpand = () => {
77
+ setIsExpanded(prev => !prev)
78
+ }
79
+
80
+ return (
81
+ <div className="overflow-hidden rounded-lg border border-blue-200 bg-blue-50">
82
+ {/* 标题 */}
83
+ <div className="flex items-center gap-2 border-b border-blue-200 bg-blue-100 px-3 py-2">
84
+ <svg
85
+ width="18"
86
+ height="18"
87
+ viewBox="0 0 24 24"
88
+ fill="none"
89
+ stroke="currentColor"
90
+ strokeWidth="2"
91
+ strokeLinecap="round"
92
+ strokeLinejoin="round"
93
+ className="shrink-0 text-blue-700"
94
+ >
95
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
96
+ <polyline points="14 2 14 8 20 8" />
97
+ <line x1="16" y1="13" x2="8" y2="13" />
98
+ <line x1="16" y1="17" x2="8" y2="17" />
99
+ <polyline points="10 9 9 9 8 9" />
100
+ </svg>
101
+ <h3 className="text-sm font-semibold text-blue-900">{title}</h3>
102
+ </div>
103
+
104
+ {/* 内容 */}
105
+ <div className="px-3 py-2">
106
+ <div
107
+ className={`
108
+ text-sm leading-relaxed text-blue-900
109
+ ${!isExpanded && isLong ? 'line-clamp-3' : ''}
110
+ `}
111
+ >
112
+ <ReactMarkdown
113
+ remarkPlugins={[remarkGfm]}
114
+ components={{
115
+ p: ({ node, ...props }) => <p {...props} className="mb-2 last:mb-0" />,
116
+ ul: ({ node, ...props }) => <ul {...props} className="mb-2 ml-4 list-disc" />,
117
+ ol: ({ node, ...props }) => <ol {...props} className="mb-2 ml-4 list-decimal" />,
118
+ li: ({ node, ...props }) => <li {...props} className="mb-1" />,
119
+ strong: ({ node, ...props }) => <strong {...props} className="font-bold" />,
120
+ }}
121
+ >
122
+ {content}
123
+ </ReactMarkdown>
124
+ </div>
125
+
126
+ {/* 展开/收起按钮(仅长内容显示) */}
127
+ {isLong && (
128
+ <button
129
+ type="button"
130
+ onClick={toggleExpand}
131
+ className="mt-2 flex items-center gap-1 text-xs font-medium text-blue-700 hover:text-blue-800"
132
+ >
133
+ <span>{isExpanded ? '收起' : '展开'}</span>
134
+ <svg
135
+ width="14"
136
+ height="14"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ stroke="currentColor"
140
+ strokeWidth="2"
141
+ strokeLinecap="round"
142
+ strokeLinejoin="round"
143
+ className={`transition-transform ${isExpanded ? 'rotate-180' : ''}`}
144
+ >
145
+ <polyline points="6 9 12 15 18 9" />
146
+ </svg>
147
+ </button>
148
+ )}
149
+ </div>
150
+ </div>
151
+ )
152
+ }