diff --git a/src/api/admin/assets.ts b/src/api/admin/assets.ts index 9311b07..9505663 100644 --- a/src/api/admin/assets.ts +++ b/src/api/admin/assets.ts @@ -304,3 +304,39 @@ export async function fetchRadarMap() { const { data } = await http.get>('/admin/v1/radar-map') return data.data } + +export interface WeeklyBriefStats { + papers_count?: number + news_count?: number + teachers_count?: number + references_count?: number + sections?: Record +} + +export interface WeeklyBriefRow { + id: number + week_start: string + week_end: string + title: string + stats?: WeeklyBriefStats | null + generated_at?: string | null +} + +export interface WeeklyBriefDetail extends WeeklyBriefRow { + markdown: string +} + +export async function fetchWeeklyBriefs(params: Record) { + const { data } = await http.get>>('/admin/v1/weekly-briefs', { params }) + return data.data +} + +export async function fetchWeeklyBrief(id: number) { + const { data } = await http.get>(`/admin/v1/weekly-briefs/${id}`) + return data.data +} + +export async function generateWeeklyBrief(payload: { week_start?: string; week_end?: string } = {}) { + const { data } = await http.post>('/admin/v1/weekly-briefs/generate', payload) + return data.data +} diff --git a/src/views/assets/crawler/index.vue b/src/views/assets/crawler/index.vue index 53fefa5..4427f05 100644 --- a/src/views/assets/crawler/index.vue +++ b/src/views/assets/crawler/index.vue @@ -11,6 +11,11 @@ import { type CrawlJobResult, type CrawlParamField, type CrawlResolveResult, + fetchWeeklyBrief, + fetchWeeklyBriefs, + generateWeeklyBrief, + type WeeklyBriefDetail, + type WeeklyBriefRow, } from '@/api/admin/assets' import { fetchDictByCode } from '@/api/admin/dict' import { ElMessage } from 'element-plus' @@ -35,6 +40,13 @@ const teacherLeadItems = ref([]) const teacherItems = ref([]) const resultLoading = ref(false) const newsCategoryOptions = ref([]) +const briefLoading = ref(false) +const briefGenerating = ref(false) +const briefItems = ref([]) +const briefMeta = ref({ current_page: 1, per_page: 10, total: 0 }) +const briefPage = ref(1) +const briefDialog = ref(false) +const briefDetail = ref(null) const form = ref({ target_type: 'paper' as TargetType, request_url: 'https://arxiv.org/', @@ -391,7 +403,84 @@ function goLibrary() { const canViewResult = () => lastResult.value?.status === 'completed' && (lastResult.value.items_fetched ?? 0) > 0 -usePageLoad(resetPage) +function formatWeekRange(start?: string | null, end?: string | null) { + if (!start || !end) return '—' + return `${start} 至 ${end}` +} + +function formatBriefGeneratedAt(iso?: string | null) { + if (!iso) return '—' + const d = new Date(iso) + if (Number.isNaN(d.getTime())) return '—' + const pad = (n: number) => String(n).padStart(2, '0') + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` +} + +async function loadBriefs() { + briefLoading.value = true + try { + const res = await fetchWeeklyBriefs({ page: briefPage.value, page_size: briefMeta.value.per_page }) + briefItems.value = res.items + briefMeta.value = res.meta + } catch { + briefItems.value = [] + } finally { + briefLoading.value = false + } +} + +async function onGenerateBrief() { + briefGenerating.value = true + try { + const brief = await generateWeeklyBrief() + ElMessage.success('周报已生成') + briefPage.value = 1 + await loadBriefs() + await openBriefDetail(brief.id) + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : '周报生成失败' + ElMessage.error(msg) + } finally { + briefGenerating.value = false + } +} + +async function openBriefDetail(id: number) { + briefDialog.value = true + briefDetail.value = null + try { + briefDetail.value = await fetchWeeklyBrief(id) + } catch { + ElMessage.error('加载周报失败') + briefDialog.value = false + } +} + +async function copyBriefMarkdown() { + if (!briefDetail.value?.markdown) return + try { + await navigator.clipboard.writeText(briefDetail.value.markdown) + ElMessage.success('已复制 Markdown') + } catch { + ElMessage.error('复制失败') + } +} + +function downloadBriefMarkdown() { + if (!briefDetail.value?.markdown) return + const blob = new Blob([briefDetail.value.markdown], { type: 'text/markdown;charset=utf-8' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `AI科技成果简报_${briefDetail.value.week_start}_${briefDetail.value.week_end}.md` + a.click() + URL.revokeObjectURL(url) +} + +usePageLoad(async () => { + await resetPage() + await loadBriefs() +})