@anker-in/campaign-ui 0.3.3 → 0.3.5
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.
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/api/chat.d.ts +23 -2
- package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
- package/dist/cjs/components/LiveChatWidget/components/ChatInput.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatInput.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
- package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
- package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/constants.d.ts +5 -0
- package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
- package/dist/cjs/components/LiveChatWidget/constants.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
- package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +36 -2
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/index.d.ts +1 -1
- package/dist/cjs/components/LiveChatWidget/index.js +1 -1
- package/dist/cjs/components/LiveChatWidget/index.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/types.d.ts +213 -3
- package/dist/cjs/components/LiveChatWidget/types.js +1 -1
- package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +2 -0
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +7 -0
- package/dist/cjs/components/chat/markdown.js +1 -1
- package/dist/cjs/components/chat/markdown.js.map +2 -2
- package/dist/cjs/components/index.d.ts +2 -0
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/components/index.js.map +3 -3
- package/dist/cjs/stories/LiveChatWidget.stories.d.ts +1 -79
- package/dist/cjs/stories/LiveChatWidget.stories.js +3 -49
- package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/api/chat.d.ts +23 -2
- package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
- package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
- package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
- package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
- package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageList.js +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/constants.d.ts +5 -0
- package/dist/esm/components/LiveChatWidget/constants.js +1 -1
- package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
- package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +36 -2
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/index.d.ts +1 -1
- package/dist/esm/components/LiveChatWidget/index.js +1 -1
- package/dist/esm/components/LiveChatWidget/index.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/types.d.ts +213 -3
- package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js +2 -0
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +7 -0
- package/dist/esm/components/chat/markdown.js +1 -1
- package/dist/esm/components/chat/markdown.js.map +2 -2
- package/dist/esm/components/index.d.ts +2 -0
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/index.js.map +3 -3
- package/dist/esm/stories/LiveChatWidget.stories.d.ts +1 -79
- package/dist/esm/stories/LiveChatWidget.stories.js +3 -49
- package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
- package/dist/index.d.mts +1305 -0
- package/dist/index.d.ts +1305 -0
- package/dist/index.js +26656 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +26641 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +8 -1
- package/src/components/LiveChatWidget/LiveChatWidget.tsx +907 -0
- package/src/components/LiveChatWidget/api/chat.ts +175 -0
- package/src/components/LiveChatWidget/components/ChatBubble.tsx +152 -0
- package/src/components/LiveChatWidget/components/ChatHeader.tsx +150 -0
- package/src/components/LiveChatWidget/components/ChatInput.tsx +253 -0
- package/src/components/LiveChatWidget/components/ChatMessage.tsx +190 -0
- package/src/components/LiveChatWidget/components/ChatWindow.tsx +363 -0
- package/src/components/LiveChatWidget/components/ComplianceDialog.tsx +216 -0
- package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +202 -0
- package/src/components/LiveChatWidget/components/MessageContent/ErrorBlock.tsx +75 -0
- package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +128 -0
- package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +152 -0
- package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +227 -0
- package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +377 -0
- package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +293 -0
- package/src/components/LiveChatWidget/components/MessageContent/PromotionList.tsx +170 -0
- package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +91 -0
- package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +110 -0
- package/src/components/LiveChatWidget/components/MessageContent/ThinkingBlock.tsx +53 -0
- package/src/components/LiveChatWidget/components/MessageContent/index.ts +16 -0
- package/src/components/LiveChatWidget/components/MessageContent.tsx +113 -0
- package/src/components/LiveChatWidget/components/MessageList.tsx +256 -0
- package/src/components/LiveChatWidget/components/ScrollAnchor.tsx +75 -0
- package/src/components/LiveChatWidget/constants.ts +36 -0
- package/src/components/LiveChatWidget/hooks/useChatAPI.ts +146 -0
- package/src/components/LiveChatWidget/hooks/useChatState.ts +1091 -0
- package/src/components/LiveChatWidget/hooks/useSession.ts +123 -0
- package/src/components/LiveChatWidget/index.tsx +63 -0
- package/src/components/LiveChatWidget/types.ts +1012 -0
- package/src/components/LiveChatWidget/utils/cartTransformers.ts +72 -0
- package/src/components/LiveChatWidget/utils/fetcher.ts +131 -0
- package/src/components/LiveChatWidget/utils/messageRenderers.ts +120 -0
- package/src/components/LiveChatWidget/utils/productTransformers.ts +149 -0
- package/src/components/LiveChatWidget/utils/userId.ts +140 -0
- package/src/components/LiveChatWidget/utils/validation.ts +99 -0
- package/src/components/chat/markdown.tsx +1 -1
- package/src/components/index.ts +23 -0
- package/src/stories/LiveChatWidget.stories.tsx +317 -0
- package/src/styles/livechat.css +346 -0
- package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
- package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
- package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
- package/dist/cjs/components/credits/context/utils/atobID.d.ts +0 -1
- package/dist/cjs/components/credits/context/utils/atobID.js +0 -2
- package/dist/cjs/components/credits/context/utils/atobID.js.map +0 -7
- package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
- package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js +0 -2
- package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
- package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
- package/dist/cjs/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
- package/dist/cjs/components/credits/context/utils/variantGetCoupon.js +0 -2
- package/dist/cjs/components/credits/context/utils/variantGetCoupon.js.map +0 -7
- package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
- package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
- package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
- package/dist/esm/components/credits/context/utils/atobID.d.ts +0 -1
- package/dist/esm/components/credits/context/utils/atobID.js +0 -2
- package/dist/esm/components/credits/context/utils/atobID.js.map +0 -7
- package/dist/esm/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
- package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js +0 -2
- package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
- package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
- package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
- package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
- package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
- package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
- package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
- package/dist/esm/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
- package/dist/esm/components/credits/context/utils/variantGetCoupon.js +0 -2
- package/dist/esm/components/credits/context/utils/variantGetCoupon.js.map +0 -7
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 聊天窗口容器组件
|
|
3
|
+
* 包含头部、消息列表、输入框的完整聊天界面
|
|
4
|
+
* 基于 specs/livechat-widget/plan.md 的窗口设计
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react'
|
|
8
|
+
import type { Message, MessageRenderer } from '../types'
|
|
9
|
+
import { ChatHeader } from './ChatHeader'
|
|
10
|
+
import { MessageList } from './MessageList'
|
|
11
|
+
import { ChatInput } from './ChatInput'
|
|
12
|
+
import { MessageRendererRegistry } from '../utils/messageRenderers'
|
|
13
|
+
|
|
14
|
+
export interface ChatWindowProps {
|
|
15
|
+
/**
|
|
16
|
+
* 消息列表
|
|
17
|
+
*/
|
|
18
|
+
messages: Message[]
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 输入框当前值
|
|
22
|
+
*/
|
|
23
|
+
inputValue: string
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 输入框值变化回调
|
|
27
|
+
*/
|
|
28
|
+
onInputChange: (value: string) => void
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 发送消息回调
|
|
32
|
+
*/
|
|
33
|
+
onSend: () => void
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 关闭窗口回调
|
|
37
|
+
*/
|
|
38
|
+
onClose?: () => void
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 新会话回调
|
|
42
|
+
*/
|
|
43
|
+
onNewSession?: () => void
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 头部标题
|
|
47
|
+
*/
|
|
48
|
+
title?: string
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Logo URL
|
|
52
|
+
*/
|
|
53
|
+
logoUrl?: string
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 是否正在发送消息
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
isSending?: boolean
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 是否正在加载历史消息
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
isLoadingHistory?: boolean
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 是否显示时间戳
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
showTimestamp?: boolean
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 是否自动滚动
|
|
75
|
+
* @default true
|
|
76
|
+
*/
|
|
77
|
+
autoScroll?: boolean
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 自定义渲染器注册表
|
|
81
|
+
*/
|
|
82
|
+
rendererRegistry?: MessageRendererRegistry
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 默认渲染器
|
|
86
|
+
*/
|
|
87
|
+
defaultRenderer?: MessageRenderer
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 输入框占位符
|
|
91
|
+
*/
|
|
92
|
+
inputPlaceholder?: string
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 自定义样式类名
|
|
96
|
+
*/
|
|
97
|
+
className?: string
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 商品添加到购物车回调
|
|
101
|
+
*/
|
|
102
|
+
onAddToCart?: (product: any) => void
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 是否显示新会话按钮
|
|
106
|
+
* @default true
|
|
107
|
+
*/
|
|
108
|
+
showNewSessionButton?: boolean
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 输入框底部提示文本
|
|
112
|
+
* 不传入则不显示
|
|
113
|
+
*/
|
|
114
|
+
bottomTips?: string
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 聊天窗口容器组件
|
|
119
|
+
*
|
|
120
|
+
* 功能:
|
|
121
|
+
* - 组合头部、消息列表、输入框
|
|
122
|
+
* - 响应式布局(移动端全屏,桌面端固定尺寸)
|
|
123
|
+
* - 动画进入/退出效果
|
|
124
|
+
*
|
|
125
|
+
* 布局结构:
|
|
126
|
+
* ```
|
|
127
|
+
* ┌─────────────────────┐
|
|
128
|
+
* │ ChatHeader │ (固定顶部)
|
|
129
|
+
* ├─────────────────────┤
|
|
130
|
+
* │ MessageList │ (可滚动区域)
|
|
131
|
+
* │ (flex-1) │
|
|
132
|
+
* ├─────────────────────┤
|
|
133
|
+
* │ ChatInput │ (固定底部)
|
|
134
|
+
* └─────────────────────┘
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* 响应式设计:
|
|
138
|
+
* - 移动端 (< 768px): 全屏显示
|
|
139
|
+
* - 平板及以上 (>= 768px): 固定尺寸弹窗
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* <ChatWindow
|
|
144
|
+
* messages={messages}
|
|
145
|
+
* inputValue={inputValue}
|
|
146
|
+
* onInputChange={setInputValue}
|
|
147
|
+
* onSend={handleSend}
|
|
148
|
+
* onClose={() => setIsOpen(false)}
|
|
149
|
+
* onNewSession={handleNewSession}
|
|
150
|
+
* isSending={isStreaming}
|
|
151
|
+
* title="AI 助手"
|
|
152
|
+
* logoUrl="/logo.png"
|
|
153
|
+
* />
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export const ChatWindow: React.FC<ChatWindowProps> = ({
|
|
157
|
+
messages,
|
|
158
|
+
inputValue,
|
|
159
|
+
onInputChange,
|
|
160
|
+
onSend,
|
|
161
|
+
onClose,
|
|
162
|
+
onNewSession,
|
|
163
|
+
title,
|
|
164
|
+
logoUrl,
|
|
165
|
+
isSending = false,
|
|
166
|
+
isLoadingHistory = false,
|
|
167
|
+
showTimestamp = true,
|
|
168
|
+
autoScroll = true,
|
|
169
|
+
rendererRegistry,
|
|
170
|
+
defaultRenderer,
|
|
171
|
+
inputPlaceholder,
|
|
172
|
+
className = '',
|
|
173
|
+
onAddToCart,
|
|
174
|
+
showNewSessionButton = true,
|
|
175
|
+
bottomTips,
|
|
176
|
+
}) => {
|
|
177
|
+
// 常量:高度限制比例
|
|
178
|
+
const MIN_HEIGHT_RATIO = 0.4 // 最小40%
|
|
179
|
+
const MAX_HEIGHT_RATIO = 0.8 // 最大80%
|
|
180
|
+
const DEFAULT_HEIGHT = 680 // 默认680px
|
|
181
|
+
|
|
182
|
+
// 获取当前允许的高度范围
|
|
183
|
+
const getHeightConstraints = useCallback(() => {
|
|
184
|
+
if (typeof window === 'undefined') {
|
|
185
|
+
return { minHeight: DEFAULT_HEIGHT, maxHeight: DEFAULT_HEIGHT }
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
minHeight: window.innerHeight * MIN_HEIGHT_RATIO,
|
|
189
|
+
maxHeight: window.innerHeight * MAX_HEIGHT_RATIO,
|
|
190
|
+
}
|
|
191
|
+
}, [MIN_HEIGHT_RATIO, MAX_HEIGHT_RATIO, DEFAULT_HEIGHT])
|
|
192
|
+
|
|
193
|
+
// 将高度限制在允许范围内
|
|
194
|
+
const clampHeight = useCallback(
|
|
195
|
+
(height: number) => {
|
|
196
|
+
const { minHeight, maxHeight } = getHeightConstraints()
|
|
197
|
+
return Math.max(minHeight, Math.min(maxHeight, height))
|
|
198
|
+
},
|
|
199
|
+
[getHeightConstraints]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// 计算初始高度:默认680px,但需要在30%-80%范围内
|
|
203
|
+
const getInitialHeight = () => {
|
|
204
|
+
if (typeof window === 'undefined') return DEFAULT_HEIGHT
|
|
205
|
+
return clampHeight(DEFAULT_HEIGHT)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 移动端高度调节状态(默认680px或屏幕80%,取较小值)
|
|
209
|
+
const [mobileHeight, setMobileHeight] = useState(getInitialHeight)
|
|
210
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
211
|
+
const dragStartY = useRef(0)
|
|
212
|
+
const dragStartHeight = useRef(getInitialHeight())
|
|
213
|
+
const windowRef = useRef<HTMLDivElement>(null)
|
|
214
|
+
|
|
215
|
+
// 更新高度并同步CSS变量
|
|
216
|
+
const updateHeight = useCallback(
|
|
217
|
+
(newHeight: number) => {
|
|
218
|
+
const clampedHeight = clampHeight(newHeight)
|
|
219
|
+
setMobileHeight(clampedHeight)
|
|
220
|
+
// 同步更新CSS变量,供 livechat.css 使用
|
|
221
|
+
if (windowRef.current) {
|
|
222
|
+
windowRef.current.style.setProperty('--livechat-mobile-height', `${clampedHeight}px`)
|
|
223
|
+
}
|
|
224
|
+
return clampedHeight
|
|
225
|
+
},
|
|
226
|
+
[clampHeight]
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// 检测是否为移动端(初始化时立即检测)
|
|
230
|
+
const [isMobile, setIsMobile] = useState(() => {
|
|
231
|
+
if (typeof window !== 'undefined') {
|
|
232
|
+
return window.innerWidth < 768
|
|
233
|
+
}
|
|
234
|
+
return false
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const handleResize = () => {
|
|
239
|
+
const newIsMobile = window.innerWidth < 768
|
|
240
|
+
setIsMobile(newIsMobile)
|
|
241
|
+
|
|
242
|
+
// 如果是移动端,检查当前高度是否在允许范围内
|
|
243
|
+
if (newIsMobile) {
|
|
244
|
+
const clampedHeight = clampHeight(mobileHeight)
|
|
245
|
+
if (clampedHeight !== mobileHeight) {
|
|
246
|
+
updateHeight(clampedHeight)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
window.addEventListener('resize', handleResize)
|
|
252
|
+
return () => window.removeEventListener('resize', handleResize)
|
|
253
|
+
}, [mobileHeight, clampHeight, updateHeight])
|
|
254
|
+
|
|
255
|
+
// 拖拽开始
|
|
256
|
+
const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => {
|
|
257
|
+
if (!isMobile) return
|
|
258
|
+
|
|
259
|
+
setIsDragging(true)
|
|
260
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
|
261
|
+
dragStartY.current = clientY
|
|
262
|
+
dragStartHeight.current = mobileHeight
|
|
263
|
+
|
|
264
|
+
// 阻止默认行为
|
|
265
|
+
e.preventDefault()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 拖拽中
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (!isDragging) return
|
|
271
|
+
|
|
272
|
+
const handleDragMove = (e: MouseEvent | TouchEvent) => {
|
|
273
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
|
274
|
+
const deltaY = dragStartY.current - clientY // 向上拖动为正值
|
|
275
|
+
const newHeight = dragStartHeight.current + deltaY
|
|
276
|
+
|
|
277
|
+
updateHeight(newHeight)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const handleDragEnd = () => {
|
|
281
|
+
setIsDragging(false)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
document.addEventListener('mousemove', handleDragMove)
|
|
285
|
+
document.addEventListener('mouseup', handleDragEnd)
|
|
286
|
+
document.addEventListener('touchmove', handleDragMove, { passive: false })
|
|
287
|
+
document.addEventListener('touchend', handleDragEnd)
|
|
288
|
+
|
|
289
|
+
return () => {
|
|
290
|
+
document.removeEventListener('mousemove', handleDragMove)
|
|
291
|
+
document.removeEventListener('mouseup', handleDragEnd)
|
|
292
|
+
document.removeEventListener('touchmove', handleDragMove)
|
|
293
|
+
document.removeEventListener('touchend', handleDragEnd)
|
|
294
|
+
}
|
|
295
|
+
}, [isDragging, updateHeight])
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<div
|
|
299
|
+
ref={windowRef}
|
|
300
|
+
className={`livechat-window flex flex-col overflow-hidden bg-white ${className}`}
|
|
301
|
+
style={
|
|
302
|
+
{
|
|
303
|
+
borderRadius: '16px',
|
|
304
|
+
borderBottomLeftRadius: isMobile && '0px',
|
|
305
|
+
borderBottomRightRadius: isMobile && '0px',
|
|
306
|
+
'--livechat-mobile-height': `${mobileHeight}px`,
|
|
307
|
+
} as React.CSSProperties
|
|
308
|
+
}
|
|
309
|
+
>
|
|
310
|
+
{/* 移动端拖拽手柄 */}
|
|
311
|
+
{isMobile && (
|
|
312
|
+
<div
|
|
313
|
+
className="flex cursor-ns-resize items-center justify-center py-2"
|
|
314
|
+
style={{
|
|
315
|
+
touchAction: 'none',
|
|
316
|
+
backgroundColor: '#ffffff',
|
|
317
|
+
}}
|
|
318
|
+
onMouseDown={handleDragStart}
|
|
319
|
+
onTouchStart={handleDragStart}
|
|
320
|
+
>
|
|
321
|
+
<div
|
|
322
|
+
style={{
|
|
323
|
+
width: '48px',
|
|
324
|
+
height: '6px',
|
|
325
|
+
borderRadius: '999px',
|
|
326
|
+
backgroundColor: '#DADCE0',
|
|
327
|
+
}}
|
|
328
|
+
/>
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
|
|
332
|
+
{/* 头部 */}
|
|
333
|
+
<ChatHeader
|
|
334
|
+
title={title}
|
|
335
|
+
logoUrl={logoUrl}
|
|
336
|
+
onClose={onClose}
|
|
337
|
+
onNewSession={onNewSession}
|
|
338
|
+
showNewSessionButton={showNewSessionButton}
|
|
339
|
+
/>
|
|
340
|
+
|
|
341
|
+
{/* 消息列表(可滚动区域) */}
|
|
342
|
+
<MessageList
|
|
343
|
+
messages={messages}
|
|
344
|
+
rendererRegistry={rendererRegistry}
|
|
345
|
+
defaultRenderer={defaultRenderer}
|
|
346
|
+
showTimestamp={showTimestamp}
|
|
347
|
+
autoScroll={autoScroll}
|
|
348
|
+
isLoadingHistory={isLoadingHistory}
|
|
349
|
+
onAddToCart={onAddToCart}
|
|
350
|
+
/>
|
|
351
|
+
|
|
352
|
+
{/* 输入框 */}
|
|
353
|
+
<ChatInput
|
|
354
|
+
value={inputValue}
|
|
355
|
+
onChange={onInputChange}
|
|
356
|
+
onSend={onSend}
|
|
357
|
+
placeholder={inputPlaceholder}
|
|
358
|
+
disabled={isSending}
|
|
359
|
+
bottomTips={bottomTips}
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 法规协议弹窗组件
|
|
3
|
+
* 在用户首次点击聊天气泡时显示,用户同意后才打开聊天窗口
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState, useCallback } from 'react'
|
|
7
|
+
import * as Dialog from '@radix-ui/react-dialog'
|
|
8
|
+
import type { ComplianceDialogConfig } from '../types'
|
|
9
|
+
|
|
10
|
+
export interface ComplianceDialogProps {
|
|
11
|
+
/**
|
|
12
|
+
* 是否打开弹窗
|
|
13
|
+
*/
|
|
14
|
+
open: boolean
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 弹窗配置
|
|
18
|
+
*/
|
|
19
|
+
config: ComplianceDialogConfig
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 用户点击同意按钮的回调
|
|
23
|
+
*/
|
|
24
|
+
onAgree: () => void
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 用户关闭弹窗的回调(点击遮罩或关闭按钮)
|
|
28
|
+
*/
|
|
29
|
+
onClose: () => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 法规协议弹窗组件
|
|
34
|
+
*
|
|
35
|
+
* 功能:
|
|
36
|
+
* - 显示法规协议内容
|
|
37
|
+
* - 必须勾选复选框才能点击同意按钮
|
|
38
|
+
* - 支持在文本中嵌入链接
|
|
39
|
+
* - 响应式设计,移动端友好
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <ComplianceDialog
|
|
44
|
+
* open={showCompliance}
|
|
45
|
+
* config={{
|
|
46
|
+
* title: "Hi! I'm your eufy AI assistant.",
|
|
47
|
+
* content: "AI-generated responses can be inaccurate...",
|
|
48
|
+
* checkboxText: "By starting to use \"Live Chat\", you agree to Anker's {link}.",
|
|
49
|
+
* linkText: "LIVE CHAT PRIVACY NOTICE",
|
|
50
|
+
* linkUrl: "https://www.anker.com/privacy",
|
|
51
|
+
* agreeButtonText: "Agree"
|
|
52
|
+
* }}
|
|
53
|
+
* onAgree={() => setShowCompliance(false)}
|
|
54
|
+
* onClose={() => setShowCompliance(false)}
|
|
55
|
+
* />
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export const ComplianceDialog: React.FC<ComplianceDialogProps> = ({
|
|
59
|
+
open,
|
|
60
|
+
config,
|
|
61
|
+
onAgree,
|
|
62
|
+
onClose,
|
|
63
|
+
}) => {
|
|
64
|
+
const [isChecked, setIsChecked] = useState(true)
|
|
65
|
+
|
|
66
|
+
const handleAgree = useCallback(() => {
|
|
67
|
+
if (isChecked) {
|
|
68
|
+
onAgree()
|
|
69
|
+
}
|
|
70
|
+
}, [isChecked, onAgree])
|
|
71
|
+
|
|
72
|
+
const handleOpenChange = useCallback((open: boolean) => {
|
|
73
|
+
if (!open) {
|
|
74
|
+
onClose()
|
|
75
|
+
}
|
|
76
|
+
}, [onClose])
|
|
77
|
+
|
|
78
|
+
// 处理点击事件,防止点击链接时触发 checkbox
|
|
79
|
+
const handleCheckboxLabelClick = useCallback((e: React.MouseEvent) => {
|
|
80
|
+
// 如果点击的是链接,阻止冒泡
|
|
81
|
+
if ((e.target as HTMLElement).tagName === 'A') {
|
|
82
|
+
e.stopPropagation()
|
|
83
|
+
}
|
|
84
|
+
}, [])
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Dialog.Root open={open} onOpenChange={handleOpenChange}>
|
|
88
|
+
<Dialog.Portal>
|
|
89
|
+
{/* 弹窗内容 - 无遮罩层(与聊天窗口一致) */}
|
|
90
|
+
<Dialog.Content
|
|
91
|
+
className="livechat-compliance-dialog"
|
|
92
|
+
onInteractOutside={(e) => e.preventDefault()}
|
|
93
|
+
onEscapeKeyDown={(e) => e.preventDefault()}
|
|
94
|
+
>
|
|
95
|
+
{/* Header - 包含关闭按钮(右上角) */}
|
|
96
|
+
<div className="flex items-center justify-end px-4 pt-4 pb-2 flex-shrink-0 max-md:px-6 max-sm:px-5">
|
|
97
|
+
<Dialog.Close asChild>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
className="
|
|
101
|
+
w-6 h-6
|
|
102
|
+
flex items-center justify-center
|
|
103
|
+
border-0 bg-transparent text-gray-500
|
|
104
|
+
cursor-pointer
|
|
105
|
+
"
|
|
106
|
+
aria-label="关闭"
|
|
107
|
+
>
|
|
108
|
+
<svg
|
|
109
|
+
width="20"
|
|
110
|
+
height="20"
|
|
111
|
+
className="text-[#3C3C3B]"
|
|
112
|
+
viewBox="0 0 20 20"
|
|
113
|
+
fill="none"
|
|
114
|
+
stroke="currentColor"
|
|
115
|
+
strokeWidth="2"
|
|
116
|
+
strokeLinecap="round"
|
|
117
|
+
strokeLinejoin="round"
|
|
118
|
+
>
|
|
119
|
+
<line x1="15" y1="5" x2="5" y2="15" />
|
|
120
|
+
<line x1="5" y1="5" x2="15" y2="15" />
|
|
121
|
+
</svg>
|
|
122
|
+
</button>
|
|
123
|
+
</Dialog.Close>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{/* Body - 可滚动内容区域 */}
|
|
127
|
+
<div className="flex-1 overflow-y-auto px-4 pb-4 max-md:px-6 max-md:pb-6 max-sm:px-5 max-sm:pb-5">
|
|
128
|
+
{/* 标题 */}
|
|
129
|
+
<Dialog.Title
|
|
130
|
+
className="
|
|
131
|
+
text-[18px] font-bold text-[#080A0F]
|
|
132
|
+
mb-1 leading-[1.4] tracking-tight
|
|
133
|
+
"
|
|
134
|
+
>
|
|
135
|
+
{config.title}
|
|
136
|
+
</Dialog.Title>
|
|
137
|
+
|
|
138
|
+
{/* 内容 */}
|
|
139
|
+
<div
|
|
140
|
+
className="
|
|
141
|
+
text-[16px] font-bold leading-[1.4] text-[#080A0F]
|
|
142
|
+
tracking-tight
|
|
143
|
+
mb-2
|
|
144
|
+
"
|
|
145
|
+
dangerouslySetInnerHTML={{ __html: config.content }}
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
{/* 勾选框 */}
|
|
149
|
+
<label
|
|
150
|
+
className="
|
|
151
|
+
flex items-center gap-2 mb-4
|
|
152
|
+
cursor-pointer select-none
|
|
153
|
+
"
|
|
154
|
+
onClick={handleCheckboxLabelClick}
|
|
155
|
+
>
|
|
156
|
+
<input
|
|
157
|
+
type="checkbox"
|
|
158
|
+
className="absolute opacity-0 w-0 h-0"
|
|
159
|
+
checked={isChecked}
|
|
160
|
+
onChange={e => setIsChecked(e.target.checked)}
|
|
161
|
+
/>
|
|
162
|
+
<span
|
|
163
|
+
className={`
|
|
164
|
+
flex-shrink-0 w-4 h-4 mt-0.5
|
|
165
|
+
border-[1px] rounded-[2px] flex items-center justify-center
|
|
166
|
+
transition-all duration-200 ease-in-out
|
|
167
|
+
${isChecked ? 'bg-[#080A0F] border-[#080A0F] text-white' : 'bg-white border-[#2A2C32]'}
|
|
168
|
+
`}
|
|
169
|
+
>
|
|
170
|
+
{isChecked && (
|
|
171
|
+
<svg
|
|
172
|
+
width="12"
|
|
173
|
+
height="10"
|
|
174
|
+
viewBox="0 0 12 10"
|
|
175
|
+
fill="none"
|
|
176
|
+
stroke="currentColor"
|
|
177
|
+
strokeWidth="2"
|
|
178
|
+
strokeLinecap="round"
|
|
179
|
+
strokeLinejoin="round"
|
|
180
|
+
>
|
|
181
|
+
<polyline points="1.5 5 4.5 8 10.5 2" />
|
|
182
|
+
</svg>
|
|
183
|
+
)}
|
|
184
|
+
</span>
|
|
185
|
+
<span
|
|
186
|
+
className="
|
|
187
|
+
flex-1 text-[14px] leading-[1.4] text-[#080A0F]
|
|
188
|
+
tracking-tight font-bold
|
|
189
|
+
[&_a]:underline
|
|
190
|
+
"
|
|
191
|
+
dangerouslySetInnerHTML={{ __html: config.checkboxText }}
|
|
192
|
+
/>
|
|
193
|
+
</label>
|
|
194
|
+
|
|
195
|
+
{/* 同意按钮 */}
|
|
196
|
+
<button
|
|
197
|
+
type="button"
|
|
198
|
+
className={`
|
|
199
|
+
w-full px-5 py-[10px]
|
|
200
|
+
border-0 rounded-[19px]
|
|
201
|
+
text-[14px] leading-[1.2]
|
|
202
|
+
tracking-tighter font-bold
|
|
203
|
+
transition-all duration-200 ease-in-out text-white
|
|
204
|
+
${isChecked ? 'bg-[#080A0F] cursor-pointer ' : 'bg-[#767880] cursor-not-allowed'}
|
|
205
|
+
`}
|
|
206
|
+
onClick={handleAgree}
|
|
207
|
+
disabled={!isChecked}
|
|
208
|
+
>
|
|
209
|
+
{config.agreeButtonText || 'Agree'}
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
</Dialog.Content>
|
|
213
|
+
</Dialog.Portal>
|
|
214
|
+
</Dialog.Root>
|
|
215
|
+
)
|
|
216
|
+
}
|