feat: 在AI推荐面板中显示推荐生成时间

This commit is contained in:
dmy
2026-01-22 12:51:34 +08:00
parent 4bace565e4
commit eaed16a12e
2 changed files with 56 additions and 7 deletions

View File

@@ -1,7 +1,10 @@
<template> <template>
<div class="dashboard-ai-container"> <div class="dashboard-ai-container">
<div class="dashboard-header"> <div class="dashboard-header">
<h2 class="dashboard-title">Dashboard AI</h2> <div class="dashboard-title-wrapper">
<h2 class="dashboard-title">Dashboard AI</h2>
</div>
<el-button type="primary" :loading="loading" @click="fetchAIRecommendations"> <el-button type="primary" :loading="loading" @click="fetchAIRecommendations">
<el-icon style="margin-right: 5px"><MagicStick /></el-icon> <el-icon style="margin-right: 5px"><MagicStick /></el-icon>
获取 AI 推荐 获取 AI 推荐
@@ -13,6 +16,12 @@
<el-card class="box-card" shadow="hover"> <el-card class="box-card" shadow="hover">
<div class="card-header"> <div class="card-header">
<span>AI 推荐项目</span> <span>AI 推荐项目</span>
<span v-if="lastRecommendationTime" class="last-recommendation-time">
生成时间: {{ lastRecommendationTime }}
</span>
<span v-else class="last-recommendation-time text-muted">
暂无推荐时间
</span>
<el-tag type="success">{{ aiRecommendations.length }} 个推荐</el-tag> <el-tag type="success">{{ aiRecommendations.length }} 个推荐</el-tag>
</div> </div>
<div v-if="loading" style="text-align: center; padding: 40px;"> <div v-if="loading" style="text-align: center; padding: 40px;">
@@ -129,7 +138,7 @@
<span v-else>{{ scope.row.title }}</span> <span v-else>{{ scope.row.title }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="source" label="来源" width="80" /> <el-table-column prop="source" label="来源" width="220" />
<el-table-column prop="publishDate" label="发布日期" width="95"> <el-table-column prop="publishDate" label="发布日期" width="95">
<template #default="scope"> <template #default="scope">
{{ formatDate(scope.row.publishDate) }} {{ formatDate(scope.row.publishDate) }}
@@ -172,6 +181,7 @@ const dateRange = ref<[string, string] | null>(null)
const showAllBids = ref(false) const showAllBids = ref(false)
const bidsLoading = ref(false) const bidsLoading = ref(false)
const bidsByDateRange = ref<any[]>([]) const bidsByDateRange = ref<any[]>([])
const lastRecommendationTime = ref<string | null>(null)
// 从 localStorage 加载保存的日期范围 // 从 localStorage 加载保存的日期范围
const loadSavedDateRange = () => { const loadSavedDateRange = () => {
@@ -194,7 +204,10 @@ watch(dateRange, (newDateRange) => {
const loadLatestRecommendations = async () => { const loadLatestRecommendations = async () => {
try { try {
const response = await api.get('/api/ai/latest-recommendations') const response = await api.get('/api/ai/latest-recommendations')
const recommendations = response.data const { recommendations, generatedAt } = response.data
// 更新生成时间
lastRecommendationTime.value = generatedAt
// 获取所有置顶的项目 // 获取所有置顶的项目
const pinnedResponse = await api.get('/api/bids/pinned') const pinnedResponse = await api.get('/api/bids/pinned')
@@ -289,6 +302,9 @@ const fetchAIRecommendations = async () => {
recommendations recommendations
}) })
// 更新时间戳
lastRecommendationTime.value = new Date().toLocaleString('zh-CN', { hour12: false })
ElMessage.success('AI 推荐获取成功') ElMessage.success('AI 推荐获取成功')
} catch (error: any) { } catch (error: any) {
ElMessage.error('获取 AI 推荐失败') ElMessage.error('获取 AI 推荐失败')
@@ -389,11 +405,26 @@ const togglePin = async (item: AIRecommendation) => {
gap: 10px; gap: 10px;
} }
.dashboard-title-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.dashboard-title { .dashboard-title {
margin: 0; margin: 0;
font-size: 1.25rem; font-size: 1.25rem;
} }
.last-recommendation-time {
font-size: 0.75rem;
color: var(--el-text-color-secondary);
}
.last-recommendation-time.text-muted {
color: var(--el-text-color-placeholder);
}
.ai-section { .ai-section {
margin-top: 16px; margin-top: 16px;
} }
@@ -469,6 +500,11 @@ a:hover {
align-items: flex-start; align-items: flex-start;
} }
.dashboard-title-wrapper {
flex-direction: column;
gap: 4px;
}
.dashboard-title { .dashboard-title {
font-size: 1.1rem; font-size: 1.1rem;
} }

View File

@@ -17,6 +17,7 @@ export interface AIRecommendation {
source: string; source: string;
confidence: number; confidence: number;
publishDate?: Date; publishDate?: Date;
generatedAt?: string;
} }
@Injectable() @Injectable()
@@ -127,10 +128,22 @@ ${JSON.stringify(
} }
} }
async getLatestRecommendations(): Promise<AIRecommendation[]> { async getLatestRecommendations(): Promise<{ recommendations: AIRecommendation[]; generatedAt: string | null }> {
this.logger.log('获取最新的 AI 推荐结果'); this.logger.log('获取最新的 AI 推荐结果');
try { try {
// 查询最大的 createdAt 作为生成时间
const maxCreatedAtResult = await this.aiRecommendationRepository
.createQueryBuilder('rec')
.select('MAX(rec.createdAt)', 'maxCreatedAt')
.getRawOne();
const generatedAt = maxCreatedAtResult?.maxCreatedAt
? new Date(maxCreatedAtResult.maxCreatedAt).toLocaleString('zh-CN', { hour12: false })
: null;
this.logger.log(`AI 推荐生成时间: ${generatedAt}`);
const entities = await this.aiRecommendationRepository.find({ const entities = await this.aiRecommendationRepository.find({
order: { confidence: 'DESC' }, order: { confidence: 'DESC' },
}); });
@@ -160,7 +173,7 @@ ${JSON.stringify(
); );
}); });
return result; return { recommendations: result, generatedAt };
} catch (error) { } catch (error) {
this.logger.error('获取最新 AI 推荐失败:', error); this.logger.error('获取最新 AI 推荐失败:', error);
throw error; throw error;