Commit 12faefcd654f1ab6a4bac448abff6e4fc49f98ed

Authored by chenxt
1 parent bc257923

ai语音弹窗

src/mobile/Ai/AiChatStyles.css
@@ -78,7 +78,7 @@ body { @@ -78,7 +78,7 @@ body {
78 height: 120px; 78 height: 120px;
79 position: fixed; 79 position: fixed;
80 top: 39px; 80 top: 39px;
81 - z-index: 999; 81 + z-index: 200;
82 width: 100%; 82 width: 100%;
83 } 83 }
84 .header-left h1 { 84 .header-left h1 {
@@ -97,7 +97,12 @@ body { @@ -97,7 +97,12 @@ body {
97 width: 40%; 97 width: 40%;
98 flex-wrap: wrap; 98 flex-wrap: wrap;
99 } 99 }
100 -.model-selectors, 100 +.model-Button {
  101 + position: fixed;
  102 + right: 10px;
  103 + top: 50px;
  104 + z-index: 200;
  105 +}
101 .model-selector { 106 .model-selector {
102 background: rgba(255, 255, 255, 0.1); 107 background: rgba(255, 255, 255, 0.1);
103 border: 1px solid rgba(255, 255, 255, 0.2); 108 border: 1px solid rgba(255, 255, 255, 0.2);
@@ -128,7 +133,7 @@ body { @@ -128,7 +133,7 @@ body {
128 flex: 1; 133 flex: 1;
129 overflow: hidden; 134 overflow: hidden;
130 min-height: 0; 135 min-height: 0;
131 - margin-top: 160px; 136 + margin-top: 100px;
132 } 137 }
133 .chat-main { 138 .chat-main {
134 flex: 1; 139 flex: 1;
@@ -258,13 +263,17 @@ body { @@ -258,13 +263,17 @@ body {
258 bottom: 0; 263 bottom: 0;
259 left: 50%; 264 left: 50%;
260 transform: translateX(-50%); 265 transform: translateX(-50%);
261 - z-index: 999; 266 + z-index: 200;
262 width: 100%; 267 width: 100%;
263 } 268 }
264 .input-wrapper { 269 .input-wrapper {
265 display: flex; 270 display: flex;
266 gap: 10px; 271 gap: 10px;
267 position: relative; 272 position: relative;
  273 + align-items: center;
  274 +}
  275 +.input-icon {
  276 + font-size: 30px;
268 } 277 }
269 .message-input { 278 .message-input {
270 padding: 15px 20px; 279 padding: 15px 20px;
@@ -273,7 +282,7 @@ body { @@ -273,7 +282,7 @@ body {
273 font-size: 16px; 282 font-size: 16px;
274 outline: none; 283 outline: none;
275 transition: border-color 0.3s; 284 transition: border-color 0.3s;
276 - width: 60%; 285 + width: 80%;
277 } 286 }
278 .message-input:focus { 287 .message-input:focus {
279 border-color: #667eea; 288 border-color: #667eea;
@@ -417,3 +426,40 @@ body { @@ -417,3 +426,40 @@ body {
417 height: 100%; 426 height: 100%;
418 } 427 }
419 } 428 }
  429 +.phone-model {
  430 + position: fixed;
  431 + top: 0;
  432 + left: 0;
  433 + right: 0;
  434 + z-index: 201;
  435 + width: 100%;
  436 + height: 100%;
  437 +}
  438 +.phone-model .phone-zhezhao {
  439 + position: absolute;
  440 + top: 0;
  441 + left: 0;
  442 + right: 0;
  443 + z-index: 200;
  444 + width: 100%;
  445 + height: 100%;
  446 + background-color: #fff;
  447 +}
  448 +.phone-model .phone-phone {
  449 + position: absolute;
  450 + bottom: 40px;
  451 + left: 50%;
  452 + transform: translateX(-50%);
  453 + font-size: 50px;
  454 + z-index: 210;
  455 +}
  456 +.phone-model .phone-content {
  457 + position: absolute;
  458 + width: 90%;
  459 + height: 70%;
  460 + background-color: #f3f3f3;
  461 + z-index: 210;
  462 + left: 50%;
  463 + top: 10%;
  464 + transform: translateX(-50%);
  465 +}
src/mobile/Ai/AiChatStyles.less
@@ -71,7 +71,7 @@ body { @@ -71,7 +71,7 @@ body {
71 height: 120px; 71 height: 120px;
72 position: fixed; 72 position: fixed;
73 top: 39px; 73 top: 39px;
74 - z-index: 999; 74 + z-index: 200;
75 width: 100%; 75 width: 100%;
76 } 76 }
77 77
@@ -93,7 +93,12 @@ body { @@ -93,7 +93,12 @@ body {
93 width: 40%; 93 width: 40%;
94 flex-wrap: wrap; 94 flex-wrap: wrap;
95 } 95 }
96 -.model-selectors, 96 +.model-Button {
  97 + position: fixed;
  98 + right: 10px;
  99 + top: 50px;
  100 + z-index: 200;
  101 +}
97 .model-selector { 102 .model-selector {
98 background: rgba(255,255,255,0.1); 103 background: rgba(255,255,255,0.1);
99 border: 1px solid rgba(255,255,255,0.2); 104 border: 1px solid rgba(255,255,255,0.2);
@@ -126,7 +131,7 @@ body { @@ -126,7 +131,7 @@ body {
126 flex: 1; 131 flex: 1;
127 overflow: hidden; 132 overflow: hidden;
128 min-height: 0; 133 min-height: 0;
129 - margin-top: 160px; 134 + margin-top: 100px;
130 } 135 }
131 136
132 .chat-main { 137 .chat-main {
@@ -277,7 +282,7 @@ body { @@ -277,7 +282,7 @@ body {
277 bottom: 0; 282 bottom: 0;
278 left: 50%; 283 left: 50%;
279 transform: translateX(-50%); 284 transform: translateX(-50%);
280 - z-index: 999; 285 + z-index: 200;
281 width: 100%; 286 width: 100%;
282 } 287 }
283 288
@@ -285,8 +290,11 @@ body { @@ -285,8 +290,11 @@ body {
285 display: flex; 290 display: flex;
286 gap: 10px; 291 gap: 10px;
287 position: relative; 292 position: relative;
  293 + align-items: center;
  294 +}
  295 +.input-icon{
  296 + font-size: 30px;
288 } 297 }
289 -  
290 .message-input { 298 .message-input {
291 padding: 15px 20px; 299 padding: 15px 20px;
292 border: 2px solid #e9ecef; 300 border: 2px solid #e9ecef;
@@ -294,7 +302,7 @@ body { @@ -294,7 +302,7 @@ body {
294 font-size: 16px; 302 font-size: 16px;
295 outline: none; 303 outline: none;
296 transition: border-color 0.3s; 304 transition: border-color 0.3s;
297 - width: 60%; 305 + width: 80%;
298 } 306 }
299 307
300 .message-input:focus { 308 .message-input:focus {
@@ -441,4 +449,41 @@ body { @@ -441,4 +449,41 @@ body {
441 @keyframes wave { 449 @keyframes wave {
442 0%, 100% { height: 20%; } 450 0%, 100% { height: 20%; }
443 50% { height: 100%; } 451 50% { height: 100%; }
  452 +}
  453 +.phone-model{
  454 + position: fixed;
  455 + top: 0;
  456 + left: 0;
  457 + right: 0;
  458 + z-index: 201;
  459 + width: 100%;
  460 + height: 100%;
  461 + .phone-zhezhao{
  462 + position: absolute;
  463 + top: 0;
  464 + left: 0;
  465 + right: 0;
  466 + z-index: 200;
  467 + width: 100%;
  468 + height: 100%;
  469 + background-color: #fff;
  470 + }
  471 + .phone-phone{
  472 + position: absolute;
  473 + bottom: 40px;
  474 + left: 50%;
  475 + transform: translateX(-50%);
  476 + font-size: 50px;
  477 + z-index: 210;
  478 + }
  479 + .phone-content{
  480 + position: absolute;
  481 + width: 90%;
  482 + height: 70%;
  483 + background-color: #f3f3f3;
  484 + z-index: 210;
  485 + left: 50%;
  486 + top: 10%;
  487 + transform: translateX(-50%);
  488 + }
444 } 489 }
445 \ No newline at end of file 490 \ No newline at end of file
src/mobile/Ai/newAi.jsx
1 import React, { useState, useEffect, useRef, useCallback } from 'react'; 1 import React, { useState, useEffect, useRef, useCallback } from 'react';
2 import ReactMarkdown from 'react-markdown'; 2 import ReactMarkdown from 'react-markdown';
3 import remarkGfm from 'remark-gfm'; 3 import remarkGfm from 'remark-gfm';
4 -import { AudioOutline, AudioFill } from "antd-mobile-icons"; 4 +import { Button } from "antd-mobile";
  5 +import { LocationOutline, PhoneFill } from "antd-mobile-icons";
5 import './AiChatStyles.less'; 6 import './AiChatStyles.less';
6 7
7 const ChatInterface = () => { 8 const ChatInterface = () => {
@@ -17,6 +18,7 @@ const ChatInterface = () => { @@ -17,6 +18,7 @@ const ChatInterface = () => {
17 const [welcomeContent, setWelcomeContent] = useState(''); 18 const [welcomeContent, setWelcomeContent] = useState('');
18 19
19 // 语音输入状态 20 // 语音输入状态
  21 + const [isVoiceModel, setIsVoiceModel] = useState(false);
20 const [isRecording, setIsRecording] = useState(false); 22 const [isRecording, setIsRecording] = useState(false);
21 const [isWsConnected, setIsWsConnected] = useState(false); 23 const [isWsConnected, setIsWsConnected] = useState(false);
22 const [isVoiceMode, setIsVoiceMode] = useState(false); 24 const [isVoiceMode, setIsVoiceMode] = useState(false);
@@ -205,9 +207,6 @@ const ChatInterface = () => { @@ -205,9 +207,6 @@ const ChatInterface = () => {
205 recordingTimerRef.current = setInterval(() => { 207 recordingTimerRef.current = setInterval(() => {
206 setRecordingDuration(prev => prev + 1); 208 setRecordingDuration(prev => prev + 1);
207 }, 1000); 209 }, 1000);
208 -  
209 - console.log("录音已开始");  
210 -  
211 } catch (e) { 210 } catch (e) {
212 console.error("录音启动失败:", e); 211 console.error("录音启动失败:", e);
213 alert("录音启动失败:" + e.message); 212 alert("录音启动失败:" + e.message);
@@ -407,17 +406,17 @@ const ChatInterface = () => { @@ -407,17 +406,17 @@ const ChatInterface = () => {
407 } catch (error) { 406 } catch (error) {
408 console.error('请求失败:', error); 407 console.error('请求失败:', error);
409 const errorMessage = ` 408 const errorMessage = `
410 -抱歉,请求出现错误:${error.message} 409 + 抱歉,请求出现错误:${error.message}
411 410
412 -**可能的原因:**  
413 -1. Spring Boot 后端服务未启动  
414 -2. API 接口路径不正确  
415 -3. 网络连接问题 411 + **可能的原因:**
  412 + 1. Spring Boot 后端服务未启动
  413 + 2. API 接口路径不正确
  414 + 3. 网络连接问题
416 415
417 -**检查步骤:**  
418 -1. 确保后端服务在端口 8099 运行  
419 -2. 检查浏览器控制台查看详细错误  
420 -3. 刷新页面重试 416 + **检查步骤:**
  417 + 1. 确保后端服务在端口 8099 运行
  418 + 2. 检查浏览器控制台查看详细错误
  419 + 3. 刷新页面重试
421 `; 420 `;
422 addMessage(errorMessage, 'ai', true); 421 addMessage(errorMessage, 'ai', true);
423 } finally { 422 } finally {
@@ -466,33 +465,21 @@ const ChatInterface = () => { @@ -466,33 +465,21 @@ const ChatInterface = () => {
466 const handleModelChange = (e) => { 465 const handleModelChange = (e) => {
467 setCurrentModel(e.target.value); 466 setCurrentModel(e.target.value);
468 }; 467 };
  468 + // 录音弹窗
  469 + const handlePhone = () => {
  470 + setIsVoiceModel(true)
  471 + startRecording();
  472 + }
469 473
470 // ==================== 渲染 ==================== 474 // ==================== 渲染 ====================
471 return ( 475 return (
472 <div className="ai-chat-container"> 476 <div className="ai-chat-container">
473 - {/* 头部 */}  
474 - <div className="chat-header">  
475 - <div className="header-left">  
476 - <h1>小羚羊Ai-agent智能体</h1>  
477 - <p>AI 印刷助手</p>  
478 - </div>  
479 - <div className="header-right">  
480 - <select  
481 - className="model-selector"  
482 - value={currentModel}  
483 - onChange={handleModelChange}  
484 - >  
485 - <option value="process">小羚羊印刷行业大模型</option>  
486 - <option value="general">qwen2.5:14b</option>  
487 - </select>  
488 - <button  
489 - className="model-selectors"  
490 - onClick={handleClearChat}  
491 - >  
492 - 清空对话  
493 - </button>  
494 - </div>  
495 - </div> 477 + <Button
  478 + className="model-Button"
  479 + onClick={handleClearChat}
  480 + >
  481 + 清空对话
  482 + </Button>
496 483
497 {/* 主体 */} 484 {/* 主体 */}
498 <div className="chat-body"> 485 <div className="chat-body">
@@ -570,7 +557,7 @@ const ChatInterface = () =&gt; { @@ -570,7 +557,7 @@ const ChatInterface = () =&gt; {
570 {/* 输入区域 */} 557 {/* 输入区域 */}
571 <div className="input-section"> 558 <div className="input-section">
572 {/* 语音模式提示 - 仅在录音时显示 */} 559 {/* 语音模式提示 - 仅在录音时显示 */}
573 - {isRecording && ( 560 + {/* {isRecording && (
574 <div className="voice-mode-indicator"> 561 <div className="voice-mode-indicator">
575 <div className="voice-wave"> 562 <div className="voice-wave">
576 <span></span> 563 <span></span>
@@ -589,7 +576,7 @@ const ChatInterface = () =&gt; { @@ -589,7 +576,7 @@ const ChatInterface = () =&gt; {
589 取消 576 取消
590 </button> 577 </button>
591 </div> 578 </div>
592 - )} 579 + )} */}
593 580
594 <div className="input-wrapper"> 581 <div className="input-wrapper">
595 <input 582 <input
@@ -612,37 +599,76 @@ const ChatInterface = () =&gt; { @@ -612,37 +599,76 @@ const ChatInterface = () =&gt; {
612 disabled={isLoading} 599 disabled={isLoading}
613 readOnly={isRecording} 600 readOnly={isRecording}
614 /> 601 />
  602 + <PhoneFill className='input-icon' onClick={handlePhone} />
615 603
  604 + <LocationOutline className='input-icon' onClick={handleSendMessage}
  605 + disabled={isLoading || isRecording} />
616 {/* 语音按钮 - 点击切换录音状态 */} 606 {/* 语音按钮 - 点击切换录音状态 */}
617 - <button 607 + {/* <button
618 className={`voice-button ${isRecording ? 'recording' : ''}`} 608 className={`voice-button ${isRecording ? 'recording' : ''}`}
619 onClick={toggleRecording} 609 onClick={toggleRecording}
620 disabled={isLoading} 610 disabled={isLoading}
621 > 611 >
622 {isRecording ? ( 612 {isRecording ? (
623 <> 613 <>
624 - {/* <AudioFill className="voice-icon" /> */}  
625 <span className="voice-text">结束录音</span> 614 <span className="voice-text">结束录音</span>
626 </> 615 </>
627 ) : ( 616 ) : (
628 <> 617 <>
629 - {/* <AudioOutline className="voice-icon" /> */}  
630 <span className="voice-text">点击录音</span> 618 <span className="voice-text">点击录音</span>
631 </> 619 </>
632 )} 620 )}
633 - </button> 621 + </button> */}
634 622
635 - <button 623 + {/* <button
636 className={`send-button ${isLoading ? 'disabled' : ''}`} 624 className={`send-button ${isLoading ? 'disabled' : ''}`}
637 onClick={handleSendMessage} 625 onClick={handleSendMessage}
638 disabled={isLoading || isRecording} 626 disabled={isLoading || isRecording}
639 > 627 >
640 发送 628 发送
641 - </button> 629 + </button> */}
642 </div> 630 </div>
643 </div> 631 </div>
644 </div> 632 </div>
645 </div> 633 </div>
  634 + {
  635 + isVoiceModel ?
  636 + <div className='phone-model phone-zhezhao'>
  637 + <div className='phone-zhezhao'></div>
  638 + <div className='phone-content'>
  639 +
  640 + </div>
  641 + {/* 语音模式提示 - 仅在录音时显示 */}
  642 + {isRecording && (
  643 + <div className="voice-mode-indicator">
  644 + <div className="voice-wave">
  645 + <span></span>
  646 + <span></span>
  647 + <span></span>
  648 + <span></span>
  649 + <span></span>
  650 + </div>
  651 + <span className="voice-text">
  652 + 正在录音 {formatDuration(recordingDuration)}
  653 + </span>
  654 + <button
  655 + className="voice-cancel-btn"
  656 + onClick={cancelRecording}
  657 + >
  658 + 取消
  659 + </button>
  660 + </div>
  661 + )}
  662 + <div className='phone-phone' onClick={() => {
  663 + stopRecording()
  664 + setIsVoiceModel(false)
  665 + }}>
  666 + <PhoneFill color='red' />
  667 + </div>
  668 +
  669 + </div> : ''
  670 + }
  671 +
646 </div> 672 </div>
647 ); 673 );
648 }; 674 };
src/mobile/common/CommobileList.js
@@ -136,7 +136,7 @@ class CommobileList extends React.Component { @@ -136,7 +136,7 @@ class CommobileList extends React.Component {
136 {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions 136 {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
137 onClick={() => this.props.onCopyToClick(rowData)} */} 137 onClick={() => this.props.onCopyToClick(rowData)} */}
138 {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */} 138 {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
139 - <li key={rowID} style={{ listStyle: 'none' }} onDoubleClick={() => this.handleDoubleClick(rowData)} > 139 + <li key={rowID} style={{ listStyle: 'none' }} >
140 { 140 {
141 true ? 141 true ?
142 <div style={{ display: 'flex', justifyContent: 'space-between' }}> 142 <div style={{ display: 'flex', justifyContent: 'space-between' }}>
src/routes/mobile/IndexMobile.js
@@ -515,7 +515,7 @@ class IndexMobile extends React.Component { @@ -515,7 +515,7 @@ class IndexMobile extends React.Component {
515 commonBusiness.clearSocketData({ token, value: {}, sModelsId: 100 }); 515 commonBusiness.clearSocketData({ token, value: {}, sModelsId: 100 });
516 this.setState({ 516 this.setState({
517 selectedTab: "ai", 517 selectedTab: "ai",
518 - title: ai, 518 + title: '小羚羊智能体',
519 }); 519 });
520 history.push("/indexMobile/ai"); 520 history.push("/indexMobile/ai");
521 }} 521 }}
src/utils/config.js
@@ -9,7 +9,7 @@ const API = process.env.API; @@ -9,7 +9,7 @@ const API = process.env.API;
9 const bHttps = false; 9 const bHttps = false;
10 export const webSite = { 10 export const webSite = {
11 faceAddress: isDev ? '//km5cjx.gnway.cc:36867/xlyFace' : '//' + location.host + '/xlyFace', 11 faceAddress: isDev ? '//km5cjx.gnway.cc:36867/xlyFace' : '//' + location.host + '/xlyFace',
12 - ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//km5cjx.gnway.cc:37845/xlyEntry/' : '//' + location.host + '/xlyEntry/', 12 + ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//118.178.19.35:9198/xlyEntry_saas/' : '//' + location.host + '/xlyEntry/',
13 13
14 // ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//118.178.19.35:8088/xlyEntry/' : '//' + location.host + '/xlyEntry/', 14 // ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//118.178.19.35:8088/xlyEntry/' : '//' + location.host + '/xlyEntry/',
15 // ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//km5cjx.gnway.cc:36867/xlyEntry/' : '//' + location.host + '/xlyEntry/', 15 // ipAddress: localStorage.ipAddress ? localStorage.ipAddress : isDev ? '//km5cjx.gnway.cc:36867/xlyEntry/' : '//' + location.host + '/xlyEntry/',