完成:项目开发
This commit is contained in:
@@ -149,6 +149,108 @@ html {
|
||||
.message-text {
|
||||
flex: 1;
|
||||
white-space: pre-wrap;
|
||||
|
||||
.message-markdown {
|
||||
// 通用Markdown样式
|
||||
.markdown-paragraph {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.markdown-list-item,
|
||||
.markdown-ordered-list-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.markdown-heading {
|
||||
margin: 12px 0 6px 0;
|
||||
|
||||
&.markdown-h1 {
|
||||
font-size: 1.2rem;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
&.markdown-h2 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
&.markdown-h3 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// AI消息特定样式
|
||||
&.ai-markdown {
|
||||
.markdown-code-block {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
margin: 8px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.markdown-inline-code {
|
||||
background-color: #f1f3f4;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9rem;
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.markdown-blockquote {
|
||||
border-left: 3px solid #3498db;
|
||||
padding-left: 8px;
|
||||
margin: 8px 0;
|
||||
background-color: rgba(52, 152, 219, 0.1);
|
||||
}
|
||||
|
||||
.markdown-link {
|
||||
color: #3498db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// 用户消息特定样式
|
||||
&.user-markdown {
|
||||
.markdown-code-block {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
margin: 8px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.markdown-inline-code {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.markdown-blockquote {
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.5);
|
||||
padding-left: 8px;
|
||||
margin: 8px 0;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.markdown-link {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-bold {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.markdown-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.typing-cursor {
|
||||
@@ -194,10 +296,8 @@ html {
|
||||
|
||||
.message-input {
|
||||
width: 100%;
|
||||
min-height: 90px;
|
||||
max-height: 120px;
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border: 2px solid #424242;
|
||||
border-radius: 20px;
|
||||
font-size: 1rem; // 使用 rem 单位
|
||||
line-height: 1.8;
|
||||
@@ -249,7 +349,7 @@ html {
|
||||
|
||||
.stop-button {
|
||||
min-width: 60px;
|
||||
height: 40px;
|
||||
height: 60px;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
|
||||
border: none;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import {useEffect, useState, useRef} from "react";
|
||||
import {View, Textarea} from '@tarojs/components';
|
||||
import {Textarea} from '@tarojs/components';
|
||||
import Taro from '@tarojs/taro';
|
||||
import {Button} from '@nutui/nutui-react-taro';
|
||||
import {User, ArrowUp, Home} from '@nutui/icons-react-taro';
|
||||
import {sendAiMessage, stopAiMessage} from '@/api/ai';
|
||||
import {User, Home} from '@nutui/icons-react-taro';
|
||||
import {sendAiMessage} from '@/api/ai';
|
||||
import {createWebSocket} from '@/utils/websocket';
|
||||
import {getAiToken} from '@/utils/aiToken';
|
||||
import MarkdownRenderer from '@/components/MarkdownRenderer';
|
||||
// 显示html富文本
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import './index.scss';
|
||||
import {WSS_API_URL} from "@/utils/server";
|
||||
|
||||
@@ -27,7 +30,7 @@ const AiChat = () => {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [currentTaskId, setCurrentTaskId] = useState<string>('');
|
||||
// const [currentTaskId, setCurrentTaskId] = useState<string>('');
|
||||
const [wsConnected, setWsConnected] = useState(false);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
@@ -84,7 +87,7 @@ const AiChat = () => {
|
||||
msg.isTyping ? {...msg, isTyping: false} : msg
|
||||
));
|
||||
setIsLoading(false);
|
||||
setCurrentTaskId('');
|
||||
// setCurrentTaskId('');
|
||||
} else {
|
||||
// 实时更新AI回复消息
|
||||
setMessages(prev => {
|
||||
@@ -109,7 +112,7 @@ const AiChat = () => {
|
||||
});
|
||||
|
||||
if (data.taskId) {
|
||||
setCurrentTaskId(data.taskId);
|
||||
// setCurrentTaskId(data.taskId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,20 +273,20 @@ const AiChat = () => {
|
||||
};
|
||||
|
||||
// 停止AI回复
|
||||
const handleStopMessage = async () => {
|
||||
if (currentTaskId) {
|
||||
try {
|
||||
await stopAiMessage({taskId: currentTaskId});
|
||||
setIsLoading(false);
|
||||
setCurrentTaskId('');
|
||||
setMessages(prev => prev.map(msg =>
|
||||
msg.isTyping ? {...msg, isTyping: false} : msg
|
||||
));
|
||||
} catch (error) {
|
||||
console.error('停止消息失败:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
// const handleStopMessage = async () => {
|
||||
// if (currentTaskId) {
|
||||
// try {
|
||||
// await stopAiMessage({taskId: currentTaskId});
|
||||
// setIsLoading(false);
|
||||
// setCurrentTaskId('');
|
||||
// setMessages(prev => prev.map(msg =>
|
||||
// msg.isTyping ? {...msg, isTyping: false} : msg
|
||||
// ));
|
||||
// } catch (error) {
|
||||
// console.error('停止消息失败:', error);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// 处理快捷问题点击
|
||||
const handleQuickQuestion = (question: string) => {
|
||||
@@ -341,7 +344,10 @@ const AiChat = () => {
|
||||
</View>
|
||||
<View className="message-content">
|
||||
<View className="message-text">
|
||||
{message.query || (message.isTyping && message.type === 'ai' ? '正在思考中...' : '')}
|
||||
<MarkdownRenderer
|
||||
content={message.query || (message.isTyping && message.type === 'ai' ? '正在思考中...' : '')}
|
||||
className={`message-markdown ${message.type === 'user' ? 'user-markdown' : 'ai-markdown'}`}
|
||||
/>
|
||||
</View>
|
||||
{message.isTyping && message.type === 'ai' && message.query && (
|
||||
<View className="typing-cursor">|</View>
|
||||
@@ -365,7 +371,10 @@ const AiChat = () => {
|
||||
className="question-item"
|
||||
onClick={() => handleQuickQuestion(question)}
|
||||
>
|
||||
{question}
|
||||
<RichText
|
||||
nodes={question}
|
||||
space="nbsp"
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
@@ -373,45 +382,33 @@ const AiChat = () => {
|
||||
)}
|
||||
|
||||
<View className="input-container">
|
||||
<View className="input-wrapper">
|
||||
<Textarea
|
||||
className="message-input"
|
||||
value={inputValue}
|
||||
placeholder={
|
||||
!isInitialized ? "正在初始化..." :
|
||||
isLoading ? "AI正在回复中..." :
|
||||
"请输入您的问题..."
|
||||
}
|
||||
onInput={(e) => setInputValue(e.detail.value)}
|
||||
onConfirm={() => handleSendMessage(inputValue)}
|
||||
autoHeight
|
||||
maxlength={500}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{isLoading ? (
|
||||
<Button
|
||||
onClick={handleStopMessage}
|
||||
type="primary"
|
||||
className="stop-button"
|
||||
>
|
||||
停止
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
<div className="input-wrapper flex justify-between items-center">
|
||||
<Home size={26} className={'bg-white'} onClick={() => Taro.reLaunch({url: '/pages/index/index'})}/>
|
||||
<div className={'w-full mx-2'}>
|
||||
<Textarea
|
||||
className="message-input"
|
||||
value={inputValue}
|
||||
placeholder={
|
||||
!isInitialized ? "正在初始化..." :
|
||||
isLoading ? "AI正在回复中..." :
|
||||
"请输入您的问题..."
|
||||
}
|
||||
onInput={(e) => setInputValue(e.detail.value)}
|
||||
onConfirm={() => handleSendMessage(inputValue)}
|
||||
autoHeight
|
||||
maxlength={500}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={'flex justify-center items-center pr-1'}
|
||||
onClick={() => handleSendMessage(inputValue)}
|
||||
disabled={!isInitialized || !inputValue.trim()}
|
||||
icon={<ArrowUp/>}
|
||||
className="send-button"
|
||||
>
|
||||
</Button>
|
||||
)}
|
||||
<img alt={'发送'} src={'https://oss.wsdns.cn/20250709/13424d78bb004352864051d61afe9f0e.png'} width={'30px'} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</View>
|
||||
</View>
|
||||
<View className="fixed top-2 left-3" onClick={() => Taro.reLaunch({'url': '/pages/index/index'})}>
|
||||
<Home/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user