优化:底部导航菜单

This commit is contained in:
2025-07-11 22:25:32 +08:00
parent 2a7ca140c7
commit 82d3ae5b44
4 changed files with 97 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { ApiResult } from '@/api/index'; import type {ApiResult} from '@/api/index';
/** /**
* AI聊天消息接口 * AI聊天消息接口
@@ -39,9 +39,9 @@ export async function sendAiMessage(data: AiChatMessage) {
/** /**
* 停止AI聊天 * 停止AI聊天
*/ */
export async function stopAiMessage(data: { taskId: string; type?: string }) { export async function stopAiMessage(data: { taskId: string; authCode?: string; user?: string; type?: string }) {
const res = await request.post<ApiResult<string>>( const res = await request.post<ApiResult<string>>(
'/chat/messageStop', 'https://ai-console.gxshucheng.com/ai-console-api/stop/v1',
data data
); );
if (res.code === 0) { if (res.code === 0) {

View File

@@ -11,10 +11,10 @@
.h5-tabbar-container { .h5-tabbar-container {
display: flex; display: flex;
height: 60px; height: 88px;
align-items: center; align-items: flex-end;
justify-content: space-around; justify-content: space-around;
padding: 0 16px; padding: 4px 0;
width: 100%; width: 100%;
.h5-tabbar-item { .h5-tabbar-item {
@@ -32,8 +32,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 28px;
height: 28px;
margin-bottom: 4px; margin-bottom: 4px;
transition: transform 0.3s ease; transition: transform 0.3s ease;
@@ -41,6 +39,11 @@
font-size: 20px; font-size: 20px;
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
// 确保图片图标保持正方形
image {
border-radius: 2px;
}
} }
// 特殊AI按钮样式 // 特殊AI按钮样式
@@ -48,35 +51,39 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-bottom: -8px; margin-bottom: -10px;
.h5-ai-circle { .h5-ai-circle {
width: 48px;
height: 48px;
border-radius: 50%; border-radius: 50%;
background: #ffffff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
top: -20px; top: -8px;
transition: all 0.3s ease; transition: all 0.3s ease;
.h5-ai-text { .h5-ai-text {
color: #ffffff; color: #ffffff;
font-size: 14px; font-size: 18px;
font-weight: bold; font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
} }
// 确保AI图标保持正方形并居中
image {
border-radius: 4px;
}
// 光晕效果 // 光晕效果
&::before { &::before {
background-color: #ffffff;
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.8);
content: ''; content: '';
position: absolute; position: absolute;
top: -4px; top: -10px;
left: -4px; left: -7px;
right: -4px; right: -7px;
bottom: -4px; bottom: -7px;
border-radius: 50%; border-radius: 50%;
opacity: 0; opacity: 0;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
@@ -90,19 +97,18 @@
// 文字样式 // 文字样式
.h5-tabbar-text { .h5-tabbar-text {
font-size: 12px; font-size: 18px;
color: #8a8a8a; color: #8a8a8a;
transition: color 0.3s ease; transition: color 0.3s ease;
text-align: center; text-align: center;
line-height: 1.2; line-height: 1.2;
z-index: 100;
&.selected { &.selected {
color: #d81e06;
font-weight: 500; font-weight: 500;
} }
&.special-text { &.special-text {
color: #ff6b35;
font-weight: 500; font-weight: 500;
margin-top: -12px; margin-top: -12px;
} }
@@ -126,17 +132,6 @@
opacity: 1; opacity: 1;
} }
} }
&.selected {
.h5-special-icon .h5-ai-circle {
background: linear-gradient(135deg, #ff6b35 0%, #ff4500 100%);
box-shadow: 0 6px 16px rgba(255, 69, 0, 0.5);
&::before {
background: linear-gradient(135deg, rgba(255, 107, 53, 0.5) 0%, rgba(255, 69, 0, 0.5) 100%);
}
}
}
} }
// 点击效果 // 点击效果

View File

@@ -1,8 +1,7 @@
import {useState, useEffect} from 'react'; import {useState, useEffect} from 'react';
import {View, Text} from '@tarojs/components'; import {View, Text, Image} from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import './SimpleH5TabBar.scss'; import './SimpleH5TabBar.scss';
import {Image} from '@nutui/nutui-react-taro'
interface TabBarProps { interface TabBarProps {
current?: number; current?: number;
@@ -73,12 +72,28 @@ function SimpleH5TabBar({current}: TabBarProps) {
{item.isSpecial ? ( {item.isSpecial ? (
<View className="h5-special-icon"> <View className="h5-special-icon">
<View className="h5-ai-circle"> <View className="h5-ai-circle">
<Image src={item.icon} width={'24px'} height={'24px'}/> <Image
src={item.icon}
style={{
width: '68px',
height: '68px',
objectFit: 'contain'
}}
mode="aspectFit"
/>
</View> </View>
</View> </View>
) : ( ) : (
<View className="h5-normal-icon"> <View className="h5-normal-icon">
<Image src={item.icon} width={'24px'} height={'24px'}/> <Image
src={item.icon}
style={{
width: '28px',
height: '28px',
objectFit: 'contain'
}}
mode="aspectFit"
/>
</View> </View>
)} )}
<Text <Text

View File

@@ -3,7 +3,7 @@ import {Textarea} from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import {Button} from '@nutui/nutui-react-taro'; import {Button} from '@nutui/nutui-react-taro';
import {User, Home} from '@nutui/icons-react-taro'; import {User, Home} from '@nutui/icons-react-taro';
import {sendAiMessage} from '@/api/ai'; import {sendAiMessage, stopAiMessage} from '@/api/ai';
import {createWebSocket} from '@/utils/websocket'; import {createWebSocket} from '@/utils/websocket';
import {getAiToken} from '@/utils/aiToken'; import {getAiToken} from '@/utils/aiToken';
import MarkdownRenderer from '@/components/MarkdownRenderer'; import MarkdownRenderer from '@/components/MarkdownRenderer';
@@ -11,7 +11,6 @@ import MarkdownRenderer from '@/components/MarkdownRenderer';
import {View, RichText} from '@tarojs/components' import {View, RichText} from '@tarojs/components'
import './index.scss'; import './index.scss';
import {WSS_API_URL} from "@/utils/server"; import {WSS_API_URL} from "@/utils/server";
import SimpleH5TabBar from "@/components/SimpleH5TabBar";
// 消息类型 // 消息类型
interface Message { interface Message {
@@ -31,7 +30,7 @@ const AiChat = () => {
const [messages, setMessages] = useState<Message[]>([]); const [messages, setMessages] = useState<Message[]>([]);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
// const [currentTaskId, setCurrentTaskId] = useState<string>(''); const [currentTaskId, setCurrentTaskId] = useState<string>('');
const [wsConnected, setWsConnected] = useState(false); const [wsConnected, setWsConnected] = useState(false);
const [isInitialized, setIsInitialized] = useState(false); const [isInitialized, setIsInitialized] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null); const messagesEndRef = useRef<HTMLDivElement>(null);
@@ -79,8 +78,8 @@ const AiChat = () => {
wsRef.current = createWebSocket(WSS_API_URL + "/chat/" + userToken); wsRef.current = createWebSocket(WSS_API_URL + "/chat/" + userToken);
wsRef.current.onMessage((data: any) => { wsRef.current.onMessage((data: any) => {
console.log('收到WebSocket消息:', data); console.log('收到WebSocket消息:', data.taskId);
setCurrentTaskId(data.taskId)
if (data.answer) { if (data.answer) {
if (data.answer === '__END__') { if (data.answer === '__END__') {
// 消息结束移除typing状态 // 消息结束移除typing状态
@@ -275,20 +274,24 @@ const AiChat = () => {
}; };
// 停止AI回复 // 停止AI回复
// const handleStopMessage = async () => { const handleStop = async () => {
// if (currentTaskId) { if (currentTaskId) {
// try { try {
// await stopAiMessage({taskId: currentTaskId}); await stopAiMessage({
// setIsLoading(false); taskId: currentTaskId,
// setCurrentTaskId(''); authCode: '1fbfa21a-a3df-445e-9ca5-2c1a9eead7f4',
// setMessages(prev => prev.map(msg => user: `${Taro.getStorageSync('AI_TOKEN') || 'anonymous'}`
// msg.isTyping ? {...msg, isTyping: false} : msg });
// )); setIsLoading(false);
// } catch (error) { setCurrentTaskId('');
// console.error('停止消息失败:', error); setMessages(prev => prev.map(msg =>
// } msg.isTyping ? {...msg, isTyping: false} : msg
// } ));
// }; } catch (error) {
console.error('停止消息失败:', error);
}
}
};
// 处理快捷问题点击 // 处理快捷问题点击
const handleQuickQuestion = (question: string) => { const handleQuickQuestion = (question: string) => {
@@ -385,7 +388,8 @@ const AiChat = () => {
<View className="input-container"> <View className="input-container">
<div className="input-wrapper flex justify-between items-center"> <div className="input-wrapper flex justify-between items-center">
<Home size={26} className={'bg-white'} onClick={() => Taro.reLaunch({url: '/pages/index/index'})}/> <Home size={26} className={'bg-white'}
onClick={() => Taro.reLaunch({url: '/pages/index/index'})}/>
<div className={'w-full mx-2'}> <div className={'w-full mx-2'}>
<Textarea <Textarea
className="message-input" className="message-input"
@@ -405,10 +409,11 @@ const AiChat = () => {
className={'flex justify-center items-center pr-1'} className={'flex justify-center items-center pr-1'}
onClick={() => handleSendMessage(inputValue)} onClick={() => handleSendMessage(inputValue)}
> >
<img alt={'发送'} src={'https://oss.wsdns.cn/20250709/13424d78bb004352864051d61afe9f0e.png'} width={'30px'} /> <img alt={'发送'} src={'https://oss.wsdns.cn/20250709/13424d78bb004352864051d61afe9f0e.png'}
width={'30px'}/>
</div> </div>
</div> </div>
<Button onClick={handleStop}></Button>
</View> </View>
</View> </View>
</View> </View>