yt-chat-components 1.5.7 → 1.5.8
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/package.json +1 -1
- package/public/index.html +36 -8
- package/src/YtChatView/chatWidget/chatWindow/audioPlayer/AudioPlayer.tsx +225 -0
- package/src/YtChatView/chatWidget/chatWindow/audioPlayer/PCMAudioPlayer.js +101 -0
- package/src/YtChatView/chatWidget/chatWindow/audioRecorder/index.tsx +237 -0
- package/src/YtChatView/chatWidget/chatWindow/callInterface/index.tsx +1 -1
- package/src/YtChatView/chatWidget/chatWindow/chatMessage/index.tsx +25 -14
- package/src/YtChatView/chatWidget/chatWindow/index.tsx +96 -7
- package/src/YtChatView/chatWidget/chatWindow/inputMobile/index.module.css +134 -0
- package/src/YtChatView/chatWidget/chatWindow/inputMobile/inputMobile.js +170 -0
- package/src/YtChatView/chatWidget/index.tsx +11 -1
- package/src/YtChatView/components/TabSelector/index.jsx +102 -3
- package/src/YtChatView/components/TabSelector/index.module.css +115 -0
- package/src/YtChatView/logoBtn/index.jsx +1 -1
- package/src/YtChatView/mobileChat/index.jsx +1 -0
- package/src/YtChatView/mobileChatV2/index.jsx +712 -0
- package/src/YtChatView/mobileChatV2/index.module.css +269 -0
- package/src/YtChatView/previewDialog/index.jsx +1 -0
- package/src/YtChatView/previewDialogV2/index.jsx +28 -40
- package/src/index.tsx +2 -1
|
@@ -24,6 +24,7 @@ import {UrlCard} from "./urlCard"
|
|
|
24
24
|
import {CustomThoughtChainItem, CustomThoughtChainProps} from "./CustomThoughtChain";
|
|
25
25
|
import CustomThoughtChain from "./CustomThoughtChain";
|
|
26
26
|
import {isFunction} from "lodash";
|
|
27
|
+
import {AudioPlayer} from "../audioPlayer/AudioPlayer";
|
|
27
28
|
let speechSynth = window.speechSynthesis;
|
|
28
29
|
let utterance = null;
|
|
29
30
|
|
|
@@ -63,6 +64,7 @@ export default function ChatMessage({
|
|
|
63
64
|
key,
|
|
64
65
|
isShowReadIcon,
|
|
65
66
|
renderInitNode,
|
|
67
|
+
voice
|
|
66
68
|
}: ChatMessageType) {
|
|
67
69
|
const parseFileName = (
|
|
68
70
|
text: string,
|
|
@@ -135,24 +137,33 @@ export default function ChatMessage({
|
|
|
135
137
|
}
|
|
136
138
|
};
|
|
137
139
|
|
|
138
|
-
/**
|
|
139
|
-
* 播放文字
|
|
140
|
-
* @param text 文字
|
|
141
|
-
*/
|
|
142
140
|
const playVoice = (text)=>{
|
|
143
141
|
if (isPlay) {
|
|
144
|
-
speechSynth.cancel();
|
|
145
142
|
setIsPlay(false)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
if (window.messageAudioPlayer){
|
|
144
|
+
window.messageAudioPlayer.stop();
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (text) {
|
|
150
|
+
if (window.messageAudioPlayer){
|
|
151
|
+
window.messageAudioPlayer.stop();
|
|
152
|
+
window.messageAudioPlayer = null;
|
|
155
153
|
}
|
|
154
|
+
console.log('voice = ' + voice)
|
|
155
|
+
window.messageAudioPlayer = new AudioPlayer(voice);
|
|
156
|
+
window.messageAudioPlayer.onReady = () => {
|
|
157
|
+
console.log('已连接到语音合成服务');
|
|
158
|
+
window.messageAudioPlayer.sendRunSynthesis(text)// 尽快发送 StartSynthesis
|
|
159
|
+
};
|
|
160
|
+
window.messageAudioPlayer.onComplete = () => {
|
|
161
|
+
setIsPlay(false);
|
|
162
|
+
};
|
|
163
|
+
window.messageAudioPlayer.onError = (err) => {
|
|
164
|
+
// alert('发生错误:' + err);
|
|
165
|
+
};
|
|
166
|
+
setIsPlay(true)
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
|
|
@@ -29,7 +29,8 @@ import {isEmpty, isFunction} from 'lodash';
|
|
|
29
29
|
import btn_answer from '../../../assets/aicenter/btn_answer.png';
|
|
30
30
|
import {MethodContext} from "../../previewDialogV2";
|
|
31
31
|
import TabSelector from "../../components/TabSelector";
|
|
32
|
-
|
|
32
|
+
import {AudioRecorder} from "./audioRecorder";
|
|
33
|
+
import InputMobile from "./inputMobile/inputMobile";
|
|
33
34
|
|
|
34
35
|
let mediaRecorder = null; // 语音对象,用于录音
|
|
35
36
|
let recognition = null; // 语音识别对象
|
|
@@ -92,6 +93,9 @@ export default function ChatWindow({
|
|
|
92
93
|
isTagsHidden,
|
|
93
94
|
isEnableForV1,
|
|
94
95
|
questions,
|
|
96
|
+
voice,
|
|
97
|
+
onSmartRobotClick,
|
|
98
|
+
isShowMobileInputArea,
|
|
95
99
|
}: {
|
|
96
100
|
is_enable_call:boolean;
|
|
97
101
|
tags: [];
|
|
@@ -143,12 +147,17 @@ export default function ChatWindow({
|
|
|
143
147
|
isTagsHidden?: boolean;
|
|
144
148
|
isEnableForV1?: boolean;
|
|
145
149
|
questions?: any[];
|
|
150
|
+
voice?: string;
|
|
151
|
+
onSmartRobotClick?: Function;
|
|
152
|
+
isShowMobileInputArea?: boolean;
|
|
146
153
|
}) {
|
|
147
154
|
const ref = useRef<HTMLDivElement>(null);
|
|
148
155
|
const lastMessage = useRef<HTMLDivElement>(null);
|
|
149
156
|
const [showCallInterface, setShowCallInterface] = useState(false); // 添加通话界面状态
|
|
150
157
|
const [aiStatus, setAiStatus] = useState(null);
|
|
158
|
+
const inputMobileRef = useRef(null);
|
|
151
159
|
const inputRef = useRef<HTMLInputElement>(null); /* User input Ref */
|
|
160
|
+
const audioRecorder = useRef(null);
|
|
152
161
|
/* Initial listener for loss of focus that refocuses User input after a small delay */
|
|
153
162
|
const [nowAIContentList, setNowAIContentList] = useState<Array<MessageItem>>([]);
|
|
154
163
|
const [receivingMessage, setReceivingMessage] = useState(false);
|
|
@@ -164,7 +173,7 @@ export default function ChatWindow({
|
|
|
164
173
|
const isStream = true;//是否流式输出(手动开关)
|
|
165
174
|
const [recordState, setRecordState] = useState(false); // 录音状态。true为正在录音,false为停止录音
|
|
166
175
|
const [tagList, setTagList] = useState([]); // 问题标签列表
|
|
167
|
-
const {isTitleSideIcon, logoWidth, agentUrl, stopMessageUrl, sendMessageUrl, speakUrl} = baseConfig;
|
|
176
|
+
const {isTitleSideIcon, logoWidth, agentUrl, stopMessageUrl, sendMessageUrl, speakUrl, callUrl} = baseConfig;
|
|
168
177
|
const [inputContainerHeight, setInputContainerHeight] = useState('120px')
|
|
169
178
|
let content_id: string = null
|
|
170
179
|
let content_ns: string = null
|
|
@@ -880,6 +889,39 @@ export default function ChatWindow({
|
|
|
880
889
|
}
|
|
881
890
|
};
|
|
882
891
|
|
|
892
|
+
const stopRecord = () =>{
|
|
893
|
+
audioRecorder.current.stopRecorder();
|
|
894
|
+
audioRecorder.current = undefined;
|
|
895
|
+
setRecordState(false)
|
|
896
|
+
}
|
|
897
|
+
const startConnectAndRecord = () => {
|
|
898
|
+
if (recordState){
|
|
899
|
+
stopRecord()
|
|
900
|
+
}else {
|
|
901
|
+
setRecordState(true)
|
|
902
|
+
audioRecorder.current = new AudioRecorder();
|
|
903
|
+
audioRecorder.current.startRecorder();
|
|
904
|
+
audioRecorder.current.onTextUpdate = (text: string) => {
|
|
905
|
+
setValue(text);
|
|
906
|
+
if (inputMobileRef.current){
|
|
907
|
+
inputMobileRef.current.handleSetInputText(text);
|
|
908
|
+
}
|
|
909
|
+
if (inputRef.current) {
|
|
910
|
+
inputRef.current.value = text;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
audioRecorder.current.onRecordError = (error: string) => {
|
|
914
|
+
stopRecord()
|
|
915
|
+
};
|
|
916
|
+
audioRecorder.current.onRecordTimeout = () => {
|
|
917
|
+
stopRecord()
|
|
918
|
+
if (inputMobileRef.current){
|
|
919
|
+
inputMobileRef.current.stopRecording();
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
|
|
883
925
|
/**
|
|
884
926
|
* 输出消息时,滚动到底部
|
|
885
927
|
*/
|
|
@@ -968,9 +1010,20 @@ export default function ChatWindow({
|
|
|
968
1010
|
</div>
|
|
969
1011
|
}
|
|
970
1012
|
<TabSelector
|
|
1013
|
+
isMobile={isMobile}
|
|
1014
|
+
isSimple={isMobile}
|
|
1015
|
+
isShowByPages={true}
|
|
1016
|
+
keyShowSize={4}
|
|
1017
|
+
agentName={window_title}
|
|
1018
|
+
agentUrl={agentUrl}
|
|
971
1019
|
welcomeWords={`Hi,欢迎使用${window_title},您可以这样问我:`}
|
|
972
1020
|
handleRowClick={(word) => {
|
|
973
|
-
|
|
1021
|
+
if(!receivingMessageRef.current){
|
|
1022
|
+
handleSendMessage(word)
|
|
1023
|
+
}else{
|
|
1024
|
+
messageTip.destroy();
|
|
1025
|
+
messageTip.info("请等待回复结束后再发送")
|
|
1026
|
+
}
|
|
974
1027
|
}}
|
|
975
1028
|
dataList={questions}
|
|
976
1029
|
/>
|
|
@@ -1150,7 +1203,40 @@ export default function ChatWindow({
|
|
|
1150
1203
|
return <></>
|
|
1151
1204
|
}
|
|
1152
1205
|
|
|
1206
|
+
const renderInputAreaMobile = () => {
|
|
1207
|
+
return (
|
|
1208
|
+
<InputMobile
|
|
1209
|
+
ref={inputMobileRef}
|
|
1210
|
+
isShowVoiceButton={isShowVoiceButton}
|
|
1211
|
+
inputRef={inputRef}
|
|
1212
|
+
disabled={receivingMessage}
|
|
1213
|
+
placeholder={
|
|
1214
|
+
receivingMessage
|
|
1215
|
+
? placeholder_sending || '思考中...'
|
|
1216
|
+
: placeholder || '请输入您的问题...'
|
|
1217
|
+
}
|
|
1218
|
+
onValueChange={ value => setValue(value)}
|
|
1219
|
+
onSendClick={() => {
|
|
1220
|
+
if (receivingMessage) {
|
|
1221
|
+
addMessage({
|
|
1222
|
+
messageItemList: [...nowAIContentList],
|
|
1223
|
+
isSend: false,
|
|
1224
|
+
});
|
|
1225
|
+
abortControllerRef.current.abort('disconnect');
|
|
1226
|
+
abortControllerRef.current = new AbortController();
|
|
1227
|
+
} else {
|
|
1228
|
+
handleSendMessage()
|
|
1229
|
+
}
|
|
1230
|
+
}}
|
|
1231
|
+
onStartRecordClick={startConnectAndRecord}
|
|
1232
|
+
onStopRecordClick={stopRecord}
|
|
1233
|
+
onSmartRobotClick={onSmartRobotClick}
|
|
1234
|
+
/>
|
|
1235
|
+
)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1153
1238
|
const renderInputArea = () => {
|
|
1239
|
+
if (isMobile && isShowMobileInputArea) renderInputAreaMobile();
|
|
1154
1240
|
let isRender = messages.length === 0
|
|
1155
1241
|
if (messages.length > 0) {
|
|
1156
1242
|
const {messageItemList} = messages[messages.length - 1]
|
|
@@ -1260,7 +1346,7 @@ export default function ChatWindow({
|
|
|
1260
1346
|
<div
|
|
1261
1347
|
className="w_send_voice_box"
|
|
1262
1348
|
style={receivingMessage ? {cursor: 'not-allowed'} : {}}
|
|
1263
|
-
onClick={
|
|
1349
|
+
onClick={startConnectAndRecord}
|
|
1264
1350
|
>
|
|
1265
1351
|
<img src={recordState ? soundWavePng : (speakUrl || luyinPng)} style={{width: 35}}
|
|
1266
1352
|
className={recordState ? "w_recordIng" : ''}></img>
|
|
@@ -1275,7 +1361,7 @@ export default function ChatWindow({
|
|
|
1275
1361
|
style={receivingMessage ? {cursor: 'not-allowed'} : {}}
|
|
1276
1362
|
onClick={handleCallButtonClick}
|
|
1277
1363
|
>
|
|
1278
|
-
<img src={phonePng} style={{width:
|
|
1364
|
+
<img src={(callUrl || phonePng)} style={{width: 35}}></img>
|
|
1279
1365
|
</div>
|
|
1280
1366
|
</Tooltip>
|
|
1281
1367
|
}
|
|
@@ -1296,7 +1382,7 @@ export default function ChatWindow({
|
|
|
1296
1382
|
style={{
|
|
1297
1383
|
...(receivingMessage ? {cursor: 'pointer'} : {}),
|
|
1298
1384
|
height:'100%',
|
|
1299
|
-
padding: '0 13px 16px',
|
|
1385
|
+
padding: '0 13px 16px 0',
|
|
1300
1386
|
background: 'transparent',
|
|
1301
1387
|
display: 'flex',
|
|
1302
1388
|
alignItems: 'end',
|
|
@@ -1386,6 +1472,7 @@ export default function ChatWindow({
|
|
|
1386
1472
|
</>
|
|
1387
1473
|
)
|
|
1388
1474
|
}
|
|
1475
|
+
|
|
1389
1476
|
return (
|
|
1390
1477
|
<div
|
|
1391
1478
|
style={{...chat_window_style, width: width, height: "100%"}}
|
|
@@ -1458,6 +1545,7 @@ export default function ChatWindow({
|
|
|
1458
1545
|
handleSendMessage={handleSendMessage}
|
|
1459
1546
|
isShowReadIcon={isShowReadIcon}
|
|
1460
1547
|
renderInitNode={message.renderInitNode}
|
|
1548
|
+
voice={voice}
|
|
1461
1549
|
/>
|
|
1462
1550
|
))
|
|
1463
1551
|
}
|
|
@@ -1476,13 +1564,14 @@ export default function ChatWindow({
|
|
|
1476
1564
|
isSend={false}
|
|
1477
1565
|
handleSendMessage={handleSendMessage}
|
|
1478
1566
|
isShowReadIcon={isShowReadIcon}
|
|
1567
|
+
voice={voice}
|
|
1479
1568
|
/> : <ChatMessagePlaceholder bot_message_style={bot_message_style} aiStatus={aiStatus}/>
|
|
1480
1569
|
)
|
|
1481
1570
|
}
|
|
1482
1571
|
<div ref={lastMessage}></div>
|
|
1483
1572
|
</div>
|
|
1484
1573
|
{
|
|
1485
|
-
|
|
1574
|
+
renderInputAreaMobile()
|
|
1486
1575
|
}
|
|
1487
1576
|
</div>
|
|
1488
1577
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
.m_tool_box {
|
|
2
|
+
position: relative;
|
|
3
|
+
width: 100%;
|
|
4
|
+
height: fit-content;
|
|
5
|
+
display: flex;
|
|
6
|
+
background-color: white;
|
|
7
|
+
|
|
8
|
+
.m_avatar {
|
|
9
|
+
position: absolute;
|
|
10
|
+
left: 10px;
|
|
11
|
+
bottom: 60px;
|
|
12
|
+
/* 头像容器样式 */
|
|
13
|
+
img{
|
|
14
|
+
z-index: 10;
|
|
15
|
+
position: relative;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.m_input_box {
|
|
20
|
+
/* 输入框整体容器样式 */
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
width: 100%;
|
|
24
|
+
padding: 8px 8px;
|
|
25
|
+
background-color: #f9f9f9;
|
|
26
|
+
|
|
27
|
+
/* 在移动设备上优化触摸体验 */
|
|
28
|
+
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.m_voiceToggle {
|
|
32
|
+
/* 语音/键盘切换按钮样式 */
|
|
33
|
+
transition: background-color 0.3s ease;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.m_center_box {
|
|
37
|
+
margin-left: 8px;
|
|
38
|
+
/* 中间输入区域容器 */
|
|
39
|
+
flex: 1;
|
|
40
|
+
position: relative;
|
|
41
|
+
/* 优化移动端点击区域 */
|
|
42
|
+
-webkit-user-select: auto;
|
|
43
|
+
user-select: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.m_clickToTalk {
|
|
47
|
+
/* 点击说话样式 */
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
height: 40px;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
background-color: #f0f0f0;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
color: #666;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.m_inputBox-textarea {
|
|
59
|
+
/* 输入框样式 */
|
|
60
|
+
width: 100%;
|
|
61
|
+
font-size: 14px;
|
|
62
|
+
line-height: 20px;
|
|
63
|
+
border: none;
|
|
64
|
+
resize: none;
|
|
65
|
+
overflow-y: hidden;
|
|
66
|
+
background-color: transparent;
|
|
67
|
+
|
|
68
|
+
/* 设置输入法中文输入时的兼容样式 */
|
|
69
|
+
-webkit-box-sizing: border-box;
|
|
70
|
+
-moz-box-sizing: border-box;
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
|
|
73
|
+
/* 获得焦点时的样式 */
|
|
74
|
+
|
|
75
|
+
&:focus {
|
|
76
|
+
outline: none;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.m_sendButton {
|
|
81
|
+
/* 发送按钮样式 */
|
|
82
|
+
padding: 0px 14px;
|
|
83
|
+
height: 34px;
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
margin-left: 10px;
|
|
88
|
+
border-radius: 8px;
|
|
89
|
+
background-color: #4a90e2;
|
|
90
|
+
transition: background-color 0.3s ease;
|
|
91
|
+
|
|
92
|
+
&:active {
|
|
93
|
+
background-color: #3a80d2;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.m_sendButton {
|
|
98
|
+
/* 发送按钮样式 */
|
|
99
|
+
padding: 0px 14px;
|
|
100
|
+
height: 34px;
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
margin-left: 10px;
|
|
105
|
+
border-radius: 8px;
|
|
106
|
+
background-color: #4a90e2;
|
|
107
|
+
transition: background-color 0.3s ease;
|
|
108
|
+
|
|
109
|
+
&:active {
|
|
110
|
+
background-color: #3a80d2;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* 音波动画样式 */
|
|
115
|
+
|
|
116
|
+
.audioWave {
|
|
117
|
+
fill: #fff;
|
|
118
|
+
opacity: 1;
|
|
119
|
+
|
|
120
|
+
&.active {
|
|
121
|
+
animation: wavePulse 1s ease-out infinite;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* 音波动画关键帧 */
|
|
127
|
+
@keyframes wavePulse {
|
|
128
|
+
0%, 100% {
|
|
129
|
+
transform: scaleY(1);
|
|
130
|
+
}
|
|
131
|
+
50% {
|
|
132
|
+
transform: scaleY(1.5);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from "react";// 音波动画组件
|
|
2
|
+
import styles from "./index.module.css";
|
|
3
|
+
import {Input, message} from "antd";
|
|
4
|
+
|
|
5
|
+
class InputMobile extends React.Component {
|
|
6
|
+
constructor(props) {
|
|
7
|
+
super(props);
|
|
8
|
+
this.state = {
|
|
9
|
+
isVoice: false,
|
|
10
|
+
inputText: '',
|
|
11
|
+
isRecording: false,
|
|
12
|
+
isAudioActive: false
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
handleSetInputText = (text) => {
|
|
17
|
+
this.setState({inputText: text});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// 处理输入变化
|
|
21
|
+
handleInputChange = (e) => {
|
|
22
|
+
const {onValueChange} = this.props;
|
|
23
|
+
onValueChange(e.target.value);
|
|
24
|
+
this.setState({inputText: e.target.value}, () => {
|
|
25
|
+
// this.autoResizeTextarea();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 开始录音时激活音波动画
|
|
30
|
+
startRecording = () => {
|
|
31
|
+
if (this.props.disabled){
|
|
32
|
+
message.destroy();
|
|
33
|
+
message.warning('对话结束后重试');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.setState({
|
|
37
|
+
isRecording: true,
|
|
38
|
+
isAudioActive: true
|
|
39
|
+
});
|
|
40
|
+
// 可以在这里添加实际录音的初始化逻辑
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 停止录音时停止音波动画
|
|
44
|
+
stopRecording = () => {
|
|
45
|
+
const {onStopRecordClick} = this.props;
|
|
46
|
+
onStopRecordClick()
|
|
47
|
+
this.setState({
|
|
48
|
+
isRecording: false,
|
|
49
|
+
isAudioActive: false,
|
|
50
|
+
isVoice: false
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
handleRecordClick = () => {
|
|
55
|
+
const {onStartRecordClick} = this.props;
|
|
56
|
+
if (this.state.isVoice && !this.state.isRecording) {
|
|
57
|
+
this.startRecording();
|
|
58
|
+
onStartRecordClick()
|
|
59
|
+
} else {
|
|
60
|
+
this.stopRecording();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
render() {
|
|
65
|
+
const {isShowVoiceButton, disabled, placeholder, onSendClick, inputRef, onSmartRobotClick = () => {} } = this.props;
|
|
66
|
+
return (
|
|
67
|
+
<div className={styles.m_tool_box}>
|
|
68
|
+
{/*智能体小人图形固定显示位置*/}
|
|
69
|
+
<div className={styles.m_avatar}>
|
|
70
|
+
<img src={'https://trans-from-yuntu-resourse.oss-cn-beijing.aliyuncs.com/kai_yuan/common/smartBoy.png'} width={'60px'} style={{zIndex:10}} onClick={onSmartRobotClick}/>
|
|
71
|
+
</div>
|
|
72
|
+
{/*输入框*/}
|
|
73
|
+
<div className={styles.m_input_box} style={{display: 'flex', alignItems: 'center', width: '100%'}}>
|
|
74
|
+
{/*语音键盘切换按钮*/}
|
|
75
|
+
{
|
|
76
|
+
isShowVoiceButton &&
|
|
77
|
+
<div
|
|
78
|
+
onClick={() => this.setState({isVoice: !this.state.isVoice})}
|
|
79
|
+
className={styles.m_voiceToggle}
|
|
80
|
+
>
|
|
81
|
+
<img
|
|
82
|
+
src={this.state.isVoice ?
|
|
83
|
+
'https://trans-from-yuntu-resourse.oss-cn-beijing.aliyuncs.com/kai_yuan/common/keyboard.png' :
|
|
84
|
+
'https://trans-from-yuntu-resourse.oss-cn-beijing.aliyuncs.com/kai_yuan/common/voice.png'
|
|
85
|
+
}
|
|
86
|
+
width={'30px'}
|
|
87
|
+
height={'30px'}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
{/*输入框 或 点击说话*/}
|
|
93
|
+
<div className={styles.m_center_box} style={{flex: 1, position: 'relative'}}>
|
|
94
|
+
{this.state.isVoice ? (
|
|
95
|
+
<div
|
|
96
|
+
className={styles.m_clickToTalk}
|
|
97
|
+
style={{background: this.state.isRecording ? '#1774FF' : '#f0f0f0'}}
|
|
98
|
+
onClick={this.handleRecordClick}
|
|
99
|
+
>
|
|
100
|
+
{this.state.isRecording ? <AudioWaveAnimation isActive={true}/> : '点击说话'}
|
|
101
|
+
</div>
|
|
102
|
+
) : (
|
|
103
|
+
<Input
|
|
104
|
+
size={'large'}
|
|
105
|
+
ref={inputRef}
|
|
106
|
+
value={this.state.inputText}
|
|
107
|
+
disabled={disabled}
|
|
108
|
+
onChange={this.handleInputChange}
|
|
109
|
+
maxLength={500}
|
|
110
|
+
placeholder={placeholder}
|
|
111
|
+
className="m_inputBox-textarea"
|
|
112
|
+
style={{overflowY: 'hidden'}} // 默认隐藏滚动条
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
{/*发送按钮*/}
|
|
117
|
+
{
|
|
118
|
+
this.state.isVoice && this.state.isRecording ? (
|
|
119
|
+
<div
|
|
120
|
+
className={styles.m_sendButton}
|
|
121
|
+
style={{backgroundColor: 'red'}}
|
|
122
|
+
onClick={this.stopRecording}
|
|
123
|
+
>
|
|
124
|
+
<span style={{color: 'white', fontSize: 14}}>结束</span>
|
|
125
|
+
</div>
|
|
126
|
+
) : (
|
|
127
|
+
<div className={styles.m_sendButton} onClick={() => {
|
|
128
|
+
onSendClick();
|
|
129
|
+
this.setState({inputText: ''})
|
|
130
|
+
}}>
|
|
131
|
+
<span style={{color: 'white', fontSize: 14}}>{disabled ? '停止' : '开始'}</span>
|
|
132
|
+
</div>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
class AudioWaveAnimation extends React.Component {
|
|
142
|
+
render() {
|
|
143
|
+
const { isActive } = this.props;
|
|
144
|
+
return (
|
|
145
|
+
<svg className={`${styles.audioWave} ${isActive ? styles.active : ''}`} width="160" height="20"
|
|
146
|
+
viewBox="0 0 60 20">
|
|
147
|
+
<rect x="1" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
148
|
+
<rect x="7" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
149
|
+
<rect x="13" y="5" width="4" height="6" rx="2" ry="2"/>
|
|
150
|
+
<rect x="19" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
151
|
+
<rect x="25" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
152
|
+
<rect x="31" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
153
|
+
<rect x="37" y="5" width="4" height="6" rx="2" ry="2"/>
|
|
154
|
+
<rect x="43" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
155
|
+
<rect x="49" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
156
|
+
<rect x="1" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
157
|
+
<rect x="7" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
158
|
+
<rect x="13" y="5" width="4" height="6" rx="2" ry="2"/>
|
|
159
|
+
<rect x="19" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
160
|
+
<rect x="25" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
161
|
+
<rect x="31" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
162
|
+
<rect x="37" y="5" width="4" height="6" rx="2" ry="2"/>
|
|
163
|
+
<rect x="43" y="7" width="4" height="4" rx="2" ry="2"/>
|
|
164
|
+
<rect x="49" y="9" width="4" height="2" rx="2" ry="2"/>
|
|
165
|
+
</svg>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default InputMobile;
|
|
@@ -53,7 +53,10 @@ export default function ChatWidget({
|
|
|
53
53
|
renderCustomDropDown,
|
|
54
54
|
isTagsHidden,
|
|
55
55
|
isEnableForV1 = true,
|
|
56
|
-
questions
|
|
56
|
+
questions,
|
|
57
|
+
voice,
|
|
58
|
+
onSmartRobotClick,
|
|
59
|
+
isShowMobileInputArea,
|
|
57
60
|
}: {
|
|
58
61
|
is_enable_call:boolean,
|
|
59
62
|
tags: [];
|
|
@@ -101,6 +104,10 @@ export default function ChatWidget({
|
|
|
101
104
|
isTagsHidden: boolean;
|
|
102
105
|
isEnableForV1: boolean;
|
|
103
106
|
questions: any[];
|
|
107
|
+
isTagsHidden: boolean,
|
|
108
|
+
voice?: string;
|
|
109
|
+
onSmartRobotClick?: Function;
|
|
110
|
+
isShowMobileInputArea?: boolean;
|
|
104
111
|
}) {
|
|
105
112
|
const [open, setOpen] = useState(start_open);
|
|
106
113
|
const [messages, setMessages] = useState<ChatMessageType[]>([]);
|
|
@@ -178,6 +185,9 @@ export default function ChatWidget({
|
|
|
178
185
|
isTagsHidden={isTagsHidden}
|
|
179
186
|
isEnableForV1={isEnableForV1}
|
|
180
187
|
questions={questions}
|
|
188
|
+
voice={voice}
|
|
189
|
+
onSmartRobotClick={onSmartRobotClick}
|
|
190
|
+
isShowMobileInputArea={isShowMobileInputArea}
|
|
181
191
|
/>
|
|
182
192
|
</div>
|
|
183
193
|
);
|