This commit is contained in:
2026-06-12 11:48:17 +08:00
parent ee26bc6d7a
commit 8c93377659
45 changed files with 12786 additions and 1716 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

+16
View File
@@ -0,0 +1,16 @@
import { getRequest, postRequest } from '@/utils/request';
export const aiApi = {
hotline: () => {
return getRequest('/ai/hotline');
},
aiChat : (message, context) => {
let url = `/ai/chat?message=${encodeURIComponent(message)}`
if (context) {
url += `&context=${encodeURIComponent(JSON.stringify(context))}`
}
return getRequest(url);
},
}
-7
View File
@@ -7,11 +7,4 @@ export const messageApi = {
getMenuBadges : (param) => {
return postRequest('/message/menubadge', param);
},
aiChat : (message, context) => {
let url = `/message/chat?message=${encodeURIComponent(message)}`
if (context) {
url += `&context=${encodeURIComponent(JSON.stringify(context))}`
}
return getRequest(url);
},
}
+1 -1
View File
@@ -14,7 +14,7 @@
}
/** 按钮样式 **/
.skin-blue .ant-btn-primary {
background-color: #5FA4CC !important;
background-color: #53a7d1 !important;
color:white!important;
border:none!important;
}
+1 -1
View File
@@ -17,7 +17,7 @@
/** 按钮样式 **/
.skin-green .ant-btn-primary {
background-color:#009FA6 !important;
background-color: #23b67b !important;
color:white!important;
border:none!important;
}
+1 -1
View File
@@ -17,7 +17,7 @@
/** 按钮样式 **/
.skin-purple .ant-btn-primary {
background-color: #A8865D !important;
background-color: #9a64cf !important;
color:white!important;
border:none!important;
}
+1 -1
View File
@@ -14,7 +14,7 @@
/** 按钮样式 **/
.skin-red .ant-btn-primary {
background-color: #63B0B0 !important;
background-color: #f67f7f !important;
color: white !important;
border: none !important;
}
+1 -1
View File
@@ -14,7 +14,7 @@
/** 按钮样式 **/
.skin-yellow .ant-btn-primary {
background-color: #C87F0A !important;
background-color: #ecbe46 !important;
color: white !important;
border: none !important;
}
+1 -1
View File
@@ -41,7 +41,7 @@
.theme-dark .ant-menu-submenu-active{background-color: #304156 !important;color: #79b2ea!important; }
.theme-dark .ant-menu-item-active{background-color: #243448 !important; color: #418dea!important;}
.theme-dark .ant-menu-submenu-title {margin:4px 4px!important;padding:0px 20px!important;color: #BFCBD9!important;}
.theme-dark .ant-menu-submenu-title {margin:4px 0px!important;color: #BFCBD9!important;} /*padding:0px 20px!important;**/
.theme-dark .ant-menu-submenu-open{ color: #BFCBD9 !important;}
.theme-dart .ant-menu-submenu-expand-icon, .ant-menu-submenu-arrow{color:#BFCBD9!important; }
.theme-dark .ant-menu-submenu-selected { color: #418dea !important;}
+12 -11
View File
@@ -3,8 +3,9 @@
<div class="ai-chat-wrapper" :class="{ collapsed, resizing }" :style="wrapperStyle">
<div class="ai-chat-toggle" @click="collapsed = !collapsed">
<div class="ai-chat-toggle-inner">
<div class="ai-chat-drag-edge" @mousedown.prevent="startResize"></div>
<ZhihuiIcon :icon="collapsed ? 'DoubleLeftOutlined' : 'DoubleRightOutlined'" class="ai-chat-toggle-arrow" @click.stop="collapsed = !collapsed" />
<div class="ai-chat-drag-edge" @mousedown.stop="startResize"></div>
<ZhihuiIcon icon="DoubleLeftOutlined" v-if="collapsed" class="ai-chat-toggle-arrow"></ZhihuiIcon>
<ZhihuiIcon icon="DoubleRightOutlined" v-else class="ai-chat-toggle-arrow"></ZhihuiIcon>
</div>
</div>
<div class="ai-chat-body" v-show="!collapsed">
@@ -23,7 +24,7 @@
<!-- 浮动按钮 -->
<a-tooltip placement="left">
<a-button class="ai-chat-float-btn" :type="collapsed ? 'primary' : 'default'" shape="circle" size="large" @click="collapsed = !collapsed">
<a-button class="ai-chat-float-btn" :type="collapsed ? 'primary' : 'default'" shape="circle" size="small" @click="collapsed = !collapsed">
<template #icon><ZhihuiIcon :icon="collapsed ? 'RobotOutlined' : 'CloseOutlined'" /></template>
</a-button>
</a-tooltip>
@@ -43,7 +44,7 @@
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { ZhihuiIcon } from '@/utils/ZhihuiIcon.js'
import { messageApi } from '@/api/piao/message'
import { aiApi } from '@/api/piao/ai'
import zhihuiBus from '@/utils/zhihuiBus'
import ChatMessages from './ai-chat/ChatMessages.vue'
import ChatInput from './ai-chat/ChatInput.vue'
@@ -129,7 +130,7 @@ const sendMessage = async () => {
input.value = ''
loading.value = true
try {
const res = await messageApi.aiChat(text, pageContext.value)
const res = await aiApi.aiChat(text, pageContext.value)
messages.value.push({ role: 'assistant', content: res })
} catch {
messages.value.push({ role: 'assistant', content: '抱歉,我暂时无法回答,请稍后再试。' })
@@ -206,10 +207,10 @@ const sendMessage = async () => {
align-items: center;
justify-content: center;
flex: 1;
padding: 8px 0;
padding: 5px 0;
}
.ai-chat-toggle-arrow {
font-size: 13px;
font-size: 11px;
color: #bbb;
transition: color 0.2s, transform 0.2s;
}
@@ -266,11 +267,11 @@ const sendMessage = async () => {
}
.ai-chat-float-btn {
position: fixed;
top: 5px;
right: 100px;
top: 8px;
right: 110px;
z-index: 999;
width: 32px;
height: 32px;
width: 34px !important;
height: 34px !important;
font-size: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
+8 -5
View File
@@ -132,7 +132,7 @@
<!-- 用户名称 -->
<a-dropdown placement="bottomRight" class="user-dropdown">
<a style="color: #fffeee" class="user-dropdown-link">
<img :src="avatarSrc" class="avatarIcon" />
<img :src="avatarSrc" class="avatarIcon" @error="onAvatarError" />
<span class="user-name">{{ vueStore.getters.nickName }}</span>
<ZhihuiIcon icon="DownOutlined" />
</a>
@@ -219,9 +219,12 @@ import zhihuiAudio from '@/utils/zhihuiAudio'; // 引入音频播放器
dayjs.locale('zh-cn');
const avatarError = ref(false);
const avatarSrc = computed(() => {
if (avatarError.value) return "/images/defaultavatar.png";
return store.getters.avatar ? import.meta.env.VITE_API_DOMAIN + store.getters.avatar : "/images/defaultavatar.png";
});
const onAvatarError = () => { avatarError.value = true; };
const vueStore = useStore();
const vueRouter = useRouter();
@@ -729,12 +732,12 @@ onUnmounted(() => {
}
.avatarIcon {
height: 30px;
width: 30px;
height: 33px;
width: 33px;
border-radius: 50%;
background-color: #e4f6f6;
background-color: #a8e4e4;
opacity: 0.9;
border: 1px solid #abcbe7;
border: 1px solid #abcbe7;margin-right:5px;
}
+9 -73
View File
@@ -221,7 +221,7 @@ import { message } from 'ant-design-vue';
import { ArrowDownOutlined, ArrowUpOutlined ,TransactionOutlined,ArrowRightOutlined,SolutionOutlined,ShoppingCartOutlined,MoneyCollectOutlined,ClockCircleOutlined} from '@ant-design/icons-vue';
import { chartApi } from '@/api/piao/chart';
import { listNotice } from '@/api/system/notice';
import { routeApi } from '@/api/piao/route';
import { aiApi } from '@/api/piao/ai';
import zhihuiBus from '@/utils/zhihuiBus';
// 定义响应式数据选项
@@ -599,91 +599,27 @@ const popLoading = ref(false);
const popularLoading = computed(() => {
return popLoading.value ? {
spinning: true,
tip: 'AI正在搜索最新热门航线...',
tip: 'AI正在实时搜索最新热门航线...',
size: 'large'
} : false;
});
const popularRoutes = ref([]);
const fetchPopularRoutes = async () => {
popLoading.value = true;
let timeoutId = null;
try {
// 创建一个超时Promise
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(new Error('请求超时,请稍后重试'));
}, 10000); // 10秒超时
});
// 创建API请求Promise
const apiPromise = routeApi.popular(7);
// 使用Promise.race,哪个先完成就用哪个的结果
const res = await Promise.race([apiPromise, timeoutPromise]);
// 如果请求成功,清除超时计时器
clearTimeout(timeoutId);
// 检查响应是否包含错误
const res = await aiApi.hotline();
if (typeof res === 'string') {
try {
const parsed = JSON.parse(res);
if (parsed.error) {
// 后端返回的错误
showNotification('warning', parsed.message || '请求失败');
popularRoutes.value = fallbackRoutes;
popLoading.value = false;
return;
}
} catch (e) {
// 不是JSON错误响应,继续正常处理
}
const data = JSON.parse(res);
popularRoutes.value = Array.isArray(data) ? data : fallbackRoutes;
} else if (res && res.data) {
popularRoutes.value = Array.isArray(res.data) ? res.data : fallbackRoutes;
} else {
popularRoutes.value = fallbackRoutes;
}
// 正常处理逻辑
const events = Array.isArray(res) ? res :
(typeof res === 'string' ? res.split('\n\n').filter(line => line.startsWith('data:')) : []);
let answerData = null;
for (const event of events) {
try {
const eventData = typeof event === 'string' ?
JSON.parse(event.replace('data: ', '')) : event;
if (eventData.event === 'agent_message' && eventData.answer && eventData.answer.trim()) {
const parsed = JSON.parse(eventData.answer);
if (parsed.data) {
answerData = parsed.data;
break;
}
}
} catch (e) {
console.warn('解析事件失败:', e);
}
}
popularRoutes.value = Array.isArray(answerData) ? answerData : fallbackRoutes;
} catch (error) {
console.error('获取热门航线失败:', error);
// 清除超时计时器(如果还在)
if (timeoutId) clearTimeout(timeoutId);
// 显示错误提示
if (error.message === '请求超时,请稍后重试') {
showNotification('warning', '请求超时,网络较慢或服务器响应延迟');
} else if (error.message.includes('Network Error')) {
showNotification('error', '网络错误,请检查网络连接');
} else {
showNotification('error', '获取数据失败');
}
popularRoutes.value = fallbackRoutes;
} finally {
// 确保无论如何都会关闭loading
popLoading.value = false;
}
};