web修改ai推荐

This commit is contained in:
2025-12-25 10:48:08 +08:00
parent 878133fb40
commit a69b3edba3
3 changed files with 111 additions and 21 deletions

View File

@@ -16,13 +16,13 @@
<!-- AI助手抽屉 --> <!-- AI助手抽屉 -->
<el-drawer <el-drawer
v-model="drawerVisible" v-model="drawerVisible"
title="" title=""
direction="btt" direction="btt"
size="85%" size="90%"
:show-close="false" :show-close="false"
class="ai-drawer" class="ai-drawer"
> >
<!-- 自定义标题栏 --> <!-- 自定义标题栏 -->
<template #header> <template #header>
<div class="drawer-header"> <div class="drawer-header">
@@ -80,7 +80,9 @@
<img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="welcome-avatar" /> <img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="welcome-avatar" />
</div> </div>
<h2>你好我是{{ agentConfig?.name || 'AI助手' }}</h2> <h2>你好我是{{ agentConfig?.name || 'AI助手' }}</h2>
<AIRecommend @select="handleRecommendSelect" /> <div class="recommend-wrapper">
<AIRecommend v-if="isFirstLoad" @select="handleRecommendSelect" />
</div>
</div> </div>
<!-- 对话消息列表 --> <!-- 对话消息列表 -->
@@ -328,6 +330,8 @@ const messages = ref<AiMessage[]>([]);
const inputMessage = ref(''); const inputMessage = ref('');
const isGenerating = ref(false); const isGenerating = ref(false);
const chatContentRef = ref<HTMLElement | null>(null); const chatContentRef = ref<HTMLElement | null>(null);
// 是否是第一次加载(用于控制推荐内容只在首次加载时显示)
const isFirstLoad = ref(true);
const inputRef = ref<HTMLTextAreaElement | null>(null); const inputRef = ref<HTMLTextAreaElement | null>(null);
const uploadedFiles = ref([]); const uploadedFiles = ref([]);
@@ -381,6 +385,7 @@ function prepareNewConversation() {
currentConversation.value = null; currentConversation.value = null;
messages.value = []; messages.value = [];
showHistory.value = false; showHistory.value = false;
isFirstLoad.value = false; // 新建对话时不再显示推荐
ElMessage.success('已准备新对话'); ElMessage.success('已准备新对话');
} }
@@ -494,7 +499,6 @@ async function sendMessage() {
query: message, query: message,
files: [] files: []
}, { }, {
onStart: () => {},
onInit: (initData) => { onInit: (initData) => {
const lastMessage = messages.value[messages.value.length - 1]; const lastMessage = messages.value[messages.value.length - 1];
if (lastMessage && lastMessage.role === 'assistant') { if (lastMessage && lastMessage.role === 'assistant') {
@@ -511,7 +515,7 @@ async function sendMessage() {
} }
nextTick(() => scrollToBottom()); nextTick(() => scrollToBottom());
}, },
onDifyEvent: () => {}, onDifyEvent: () => {return},
onMessageEnd: () => { onMessageEnd: () => {
isGenerating.value = false; isGenerating.value = false;
}, },
@@ -867,6 +871,10 @@ onUnmounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
max-width: 430px;
margin: 0 auto;
width: 100%;
box-sizing: border-box;
&.with-history { &.with-history {
margin-left: 280px; margin-left: 280px;
@@ -881,7 +889,16 @@ onUnmounted(() => {
padding: 20px; padding: 20px;
text-align: center; text-align: center;
overflow-y: auto; overflow-y: auto;
width: 100%;
box-sizing: border-box;
.recommend-wrapper {
width: 100%;
max-width: 380px;
min-width: 0;
box-sizing: border-box;
}
.welcome-icon { .welcome-icon {
margin-bottom: 20px; margin-bottom: 20px;

View File

@@ -65,13 +65,13 @@
<!-- 对话内容区域 --> <!-- 对话内容区域 -->
<div class="current-chat-content" ref="chatContentRef"> <div class="current-chat-content" ref="chatContentRef">
<!-- 欢迎消息 --> <!-- 欢迎消息 -->
<div v-if="messages.length === 0" class="welcome-message"> <div v-if="!currentConversation && messages.length === 0" class="welcome-message">
<div class="welcome-icon"> <div class="welcome-icon">
<img v-if="agentAvatarUrl" :src="agentAvatarUrl" alt="AI助手" class="welcome-avatar" /> <img v-if="agentAvatarUrl" :src="agentAvatarUrl" alt="AI助手" class="welcome-avatar" />
<img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="welcome-avatar" /> <img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="welcome-avatar" />
</div> </div>
<h2>你好我是{{ agentConfig?.name || 'AI助手' }}</h2> <h2>你好我是{{ agentConfig?.name || 'AI助手' }}</h2>
<AIRecommend /> <AIRecommend v-if="isFirstLoad" />
</div> </div>
<!-- 消息列表 --> <!-- 消息列表 -->
@@ -447,6 +447,8 @@ const conversations = ref<AiConversation[]>([]);
const currentConversation = ref<AiConversation | null>(null); const currentConversation = ref<AiConversation | null>(null);
const hasMoreConversations = ref(false); const hasMoreConversations = ref(false);
const conversationPage = ref(1); const conversationPage = ref(1);
// 是否是第一次加载(用于控制推荐内容只在首次加载时显示)
const isFirstLoad = ref(true);
// 加载最近对话 // 加载最近对话
async function loadRecentConversations() { async function loadRecentConversations() {
@@ -482,6 +484,7 @@ async function loadMoreConversations() {
function prepareNewConversation() { function prepareNewConversation() {
currentConversation.value = null; currentConversation.value = null;
messages.value = []; messages.value = [];
isFirstLoad.value = false; // 新建对话时不再显示推荐
ElMessage.success('已准备新对话,发送消息后将自动创建'); ElMessage.success('已准备新对话,发送消息后将自动创建');
} }

View File

@@ -18,8 +18,8 @@
<p>暂无推荐内容</p> <p>暂无推荐内容</p>
</div> </div>
<div v-else class="list-items"> <div v-else class="list-items">
<div v-for="(item, index) in showData" :key="item.id" class="recommend-item"> <div v-for="(item, index) in showData" :key="item.id" class="recommend-item">
<span class="item-number">{{ index + 1 }}</span> <span class="item-number">{{ index + 1 }}</span>
<a :href="buildUrl(item)" class="item-link" target="_blank"> <a :href="buildUrl(item)" class="item-link" target="_blank">
<span class="item-title">{{ item.title }}</span> <span class="item-title">{{ item.title }}</span>
<span class="item-arrow"></span> <span class="item-arrow"></span>
@@ -80,6 +80,9 @@ onMounted(() => {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
box-sizing: border-box;
min-width: 0;
overflow: hidden;
.recommend-left { .recommend-left {
min-width: 80px; min-width: 80px;
@@ -109,11 +112,13 @@ onMounted(() => {
} }
.recommend-right { .recommend-right {
flex: 1; flex: 1;
background: white; background: white;
border-radius: 12px; border-radius: 12px;
padding: 16px; padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
min-width: 0;
box-sizing: border-box;
:deep(.recommend-tabs) { :deep(.recommend-tabs) {
.el-tabs__header { .el-tabs__header {
@@ -217,6 +222,7 @@ onMounted(() => {
text-decoration: none; text-decoration: none;
color: #303133; color: #303133;
gap: 12px; gap: 12px;
min-width: 0; /* 确保flex子元素不会超出父容器 */
.item-title { .item-title {
flex: 1; flex: 1;
@@ -225,6 +231,7 @@ onMounted(() => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 0; /* 确保flex子元素不会超出父容器 */
&:hover { &:hover {
color: #C62828; color: #C62828;
@@ -260,7 +267,7 @@ onMounted(() => {
@media (max-width: 768px) { @media (max-width: 768px) {
.ai-recommend { .ai-recommend {
flex-direction: column; flex-direction: column;
padding: 16px; padding: 12px;
.recommend-left { .recommend-left {
min-width: auto; min-width: auto;
@@ -271,6 +278,69 @@ onMounted(() => {
justify-content: center; justify-content: center;
} }
} }
.recommend-right {
padding: 12px;
width: 100%;
box-sizing: border-box;
:deep(.recommend-tabs) {
.el-tabs__item {
font-size: 13px;
}
}
.recommend-list {
width: 100%;
.list-items {
gap: 6px;
width: 100%;
.recommend-item {
padding: 8px 12px;
width: 100%;
box-sizing: border-box;
display: flex;
min-width: 0;
.item-number {
min-width: 20px;
height: 20px;
font-size: 10px;
margin-right: 8px;
flex-shrink: 0;
}
.item-link {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: space-between;
overflow: hidden;
.item-title {
font-size: 13px;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
}
.item-arrow {
font-size: 14px;
margin-left: 6px;
flex-shrink: 0;
opacity: 1;
}
}
}
}
}
}
} }
} }
</style> </style>