搜索、小助手推荐位

This commit is contained in:
2025-11-18 11:48:01 +08:00
parent 2ce3684711
commit 049b6f2cf3
18 changed files with 1280 additions and 23 deletions

View File

@@ -71,7 +71,7 @@
<img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="welcome-avatar" />
</div>
<h2>你好我是{{ agentConfig?.name || 'AI助手' }}</h2>
<p>{{ agentConfig?.systemPrompt || '有什么可以帮助你的吗?' }}</p>
<AIRecommend />
</div>
<!-- 消息列表 -->
@@ -189,6 +189,7 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { chatApi, chatHistoryApi, aiAgentConfigApi, fileUploadApi } from '@/apis/ai';
import type { AiConversation, AiMessage, AiAgentConfig, AiUploadFile } from '@/types/ai';
import { AIRecommend } from '@/views/public/ai';
interface AIAgentProps {
agentId?: string;

View File

@@ -0,0 +1,271 @@
<template>
<div class="ai-recommend">
<div class="recommend-left">
<div class="recommend-title">
<span class="title-icon"></span>
<span class="title-text">智能推荐</span>
</div>
</div>
<div class="recommend-right">
<!-- Tab选项卡推荐top资源和思政资源5个 -->
<el-tabs v-model="activeName" class="recommend-tabs" @tab-click="handleClick">
<el-tab-pane label="推荐top资源" name="top"></el-tab-pane>
<el-tab-pane label="思政资源" name="ideological"></el-tab-pane>
</el-tabs>
<div class="recommend-list">
<div v-if="showData.length === 0" class="empty-state">
<span class="empty-icon">📚</span>
<p>暂无推荐内容</p>
</div>
<div v-else class="list-items">
<div v-for="(item, index) in showData" :key="item.id" class="recommend-item">
<span class="item-number">{{ index + 1 }}</span>
<a :href="buildUrl(item)" class="item-link" target="_blank">
<span class="item-title">{{ item.title }}</span>
<span class="item-arrow"></span>
</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { PageParam, type ResourceRecommendVO } from '@/types'
import { resourceRecommendApi} from '@/apis/resource';
import { PUBLIC_WEB_PATH } from '@/config'
type TabName = 'top' | 'ideological'
const activeName = ref<TabName>('top')
const handleClick = (tab: any, event: Event) => {
console.log(tab, event)
loadData()
}
const showData = ref<ResourceRecommendVO[]>([])
const limit = 5
const tabMap: Record<TabName, number> = {top: 1, ideological: 2}
function buildUrl(item: ResourceRecommendVO) {
return `${PUBLIC_WEB_PATH}/article/show?articleId=${item.resourceID}`;
}
async function loadData(){
resourceRecommendApi.getRecommendsByType(tabMap[activeName.value], limit).then((res) => {
showData.value = res.dataList || []
})
}
onMounted(() => {
loadData().then(() => {
console.log()
})
})
</script>
<style lang="scss" scoped>
.ai-recommend {
width: 100%;
max-width: 900px;
margin: 0 auto;
display: flex;
flex-direction: row;
gap: 16px;
padding: 16px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
.recommend-left {
min-width: 80px;
display: flex;
align-items: center;
justify-content: center;
.recommend-title {
writing-mode: vertical-lr;
display: flex;
align-items: center;
gap: 12px;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
letter-spacing: 4px;
.title-icon {
font-size: 24px;
animation: sparkle 2s ease-in-out infinite;
}
.title-text {
padding: 10px 0;
}
}
}
.recommend-right {
flex: 1;
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
:deep(.recommend-tabs) {
.el-tabs__header {
margin-bottom: 12px;
}
.el-tabs__nav-wrap::after {
background-color: #e8eaed;
}
.el-tabs__item {
font-size: 15px;
font-weight: 500;
color: #606266;
&:hover {
color: #409eff;
}
&.is-active {
color: #409eff;
font-weight: 600;
}
}
.el-tabs__active-bar {
height: 3px;
background: linear-gradient(90deg, #409eff 0%, #66b1ff 100%);
}
}
.recommend-list {
min-height: 180px;
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: #909399;
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.6;
}
p {
font-size: 15px;
margin: 0;
}
}
.list-items {
display: flex;
flex-direction: column;
gap: 8px;
.recommend-item {
display: flex;
align-items: center;
padding: 10px 14px;
background: #f8f9fa;
border-radius: 8px;
border-left: 3px solid transparent;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
background: linear-gradient(90deg, #e3f2fd 0%, #f5f5f5 100%);
border-left-color: #409eff;
transform: translateX(4px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
.item-arrow {
transform: translateX(4px);
opacity: 1;
}
}
.item-number {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 24px;
margin-right: 12px;
font-size: 12px;
font-weight: 600;
color: #909399;
background: white;
border-radius: 50%;
flex-shrink: 0;
}
.item-link {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none;
color: #303133;
gap: 12px;
.item-title {
flex: 1;
font-size: 14px;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:hover {
color: #409eff;
}
}
.item-arrow {
font-size: 16px;
color: #409eff;
opacity: 0;
transition: all 0.3s ease;
flex-shrink: 0;
}
}
}
}
}
}
}
@keyframes sparkle {
0%, 100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
50% {
transform: scale(1.2) rotate(180deg);
opacity: 0.8;
}
}
// 响应式设计
@media (max-width: 768px) {
.ai-recommend {
flex-direction: column;
padding: 16px;
.recommend-left {
min-width: auto;
.recommend-title {
writing-mode: horizontal-tb;
flex-direction: row;
justify-content: center;
}
}
}
}
</style>

View File

@@ -1 +1,2 @@
export { default as AIAgent} from './AIAgent.vue';
export { default as AIAgent} from './AIAgent.vue';
export { default as AIRecommend} from './AIRecommend.vue';