feat: 接入内容服务端真实接口并优化页面交互

- 迁移内容数据到服务端接口,移除本地模拟数据
- 首页新增推荐课程加载与未读消息检查
- 资讯、课程、搜索页面支持分页加载与错误重试
- 登录页新增恢复课程、消息、帮助页的路由逻辑
- 评估分析页新增处理中状态动效与阶段提示
- API 环境切换到正式域名 airz.hqling.cloud
This commit is contained in:
hujun
2026-10-01 20:26:41 +08:00
parent 3b2a0139e4
commit 2a9ff403c4
41 changed files with 21742 additions and 427 deletions
+1 -48
View File
@@ -1,48 +1 @@
import { API_DEMO_ENABLED, API_BASE_URL, requestJson } from '@/services/api.uts'
import { newsArticles, financeLessons, ContentSection } from './content.uts'
export type ContentSearchResult = {
id: string, type: string, title: string, summary: string, category: string, meta: string, image: string
}
const sectionText = (sections: ContentSection[]): string => sections.map((item: ContentSection): string => item.title + ' ' + item.body).join(' ')
const matches = (text: string, words: string[]): boolean => words.every((word: string): boolean => text.toLowerCase().includes(word))
const readText = (item: UTSJSONObject, key: string): string => typeof item[key] == 'string' ? item[key] as string : ''
// type: all / news / lesson。接入后端时返回相同类型的 items,详情沿用内容 ID。
export async function searchContent(keyword: string, type: string): Promise<ContentSearchResult[]> {
const query = keyword.trim().replace(/\s+/g, ' ')
const results: ContentSearchResult[] = []
if (query.length == 0) return results
if (!API_DEMO_ENABLED) {
const response = await requestJson('/content/search', 'GET', { keyword: query, type })
const items = response['items'] as UTSJSONObject[] | null
if (items == null) throw new Error('搜索结果加载失败,请重试')
items.forEach((item: UTSJSONObject) => {
const kind = readText(item, 'type'); const id = readText(item, 'id'); const title = readText(item, 'title')
if ((kind == 'news' || kind == 'lesson') && (type == 'all' || type == kind) && id.length > 0 && title.length > 0) {
const result: ContentSearchResult = { id, type: kind, title, summary: readText(item, 'summary'), category: readText(item, 'category'), meta: readText(item, 'meta'), image: readText(item, 'image') }
results.push(result)
}
})
return results
}
const words = query.toLowerCase().split(' ')
if (type == 'all' || type == 'news') {
newsArticles.forEach((item) => {
if (matches(item.title + ' ' + item.summary + ' ' + item.category + ' ' + sectionText(item.sections), words)) {
const result: ContentSearchResult = { id: item.id, type: 'news', title: item.title, summary: item.summary, category: item.category, meta: item.date, image: item.image }
results.push(result)
}
})
}
if (type == 'all' || type == 'lesson') {
financeLessons.forEach((item) => {
if (matches(item.title + ' ' + item.desc + ' ' + item.tag + ' ' + item.intro + ' ' + sectionText(item.chapters), words)) {
const result: ContentSearchResult = { id: item.id, type: 'lesson', title: item.title, summary: item.desc, category: item.tag, meta: item.duration + ' · ' + item.chapters.length + ' 节', image: item.image }
results.push(result)
}
})
}
return results
}
export { ContentSearchResult, SearchPage, searchContent } from '@/services/content.uts'